diff --git a/.gitignore b/.gitignore index b2c30b0a..93d73b0b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ src/new/includes.rs src/new/templates/**/Cargo.lock src/new/templates/**/*.wasm src/new/templates/**/*.zip +.DS_Store diff --git a/Cargo.lock b/Cargo.lock index be8266f4..e7794434 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" diff --git a/Cargo.toml b/Cargo.toml index 31028f53..7783417f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,6 @@ wit-bindgen = "0.36.0" zip = "0.6" [workspace] -members = [] exclude = ["src/new/templates/*"] resolver = "2" diff --git a/src/main.rs b/src/main.rs index 87574074..5500718b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1050,7 +1050,7 @@ async fn make_app(current_dir: &std::ffi::OsString) -> Result { .short('t') .long("template") .help("Template to create") - .value_parser(["blank", "chat", "echo", "fibonacci", "file-transfer"]) + .value_parser(["blank", "chat", "echo", "fibonacci", "file-transfer", "llm-template"]) .default_value("chat") ) .arg(Arg::new("UI") diff --git a/src/new/mod.rs b/src/new/mod.rs index 86b6013f..e6faa3d9 100644 --- a/src/new/mod.rs +++ b/src/new/mod.rs @@ -23,6 +23,7 @@ pub enum Template { Echo, Fibonacci, FileTransfer, + LLMTemplate, } impl Language { @@ -44,6 +45,7 @@ impl Template { Template::Echo => "echo", Template::Fibonacci => "fibonacci", Template::FileTransfer => "file-transfer", + Template::LLMTemplate => "llm-template", } .to_string() } @@ -68,7 +70,8 @@ impl From<&String> for Template { "echo" => Template::Echo, "fibonacci" => Template::Fibonacci, "file-transfer" => Template::FileTransfer, - _ => panic!("kit: template must be 'blank', 'chat', 'echo', or 'fibonacci'; not '{s}'"), + "llm-template" => Template::LLMTemplate, + _ => panic!("kit: template must be 'blank', 'chat', 'echo', 'fibonacci', 'file-transfer', or 'llm-template'; not '{s}'"), } } } diff --git a/src/new/templates/rust/ui/llm-template/.gitignore b/src/new/templates/rust/ui/llm-template/.gitignore new file mode 100644 index 00000000..25b216b3 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/.gitignore @@ -0,0 +1,9 @@ +*/target/ +/target +pkg/*.wasm +pkg/ui +*.swp +*.swo +*/wasi_snapshot_preview1.wasm +*/wit/ +*/process_env diff --git a/src/new/templates/rust/ui/llm-template/Cargo.toml b/src/new/templates/rust/ui/llm-template/Cargo.toml new file mode 100644 index 00000000..d8b37ce7 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "llm-template", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/rust/ui/llm-template/api/llm-template:template.os-v0.wit b/src/new/templates/rust/ui/llm-template/api/llm-template:template.os-v0.wit new file mode 100644 index 00000000..2972a6e1 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/api/llm-template:template.os-v0.wit @@ -0,0 +1,110 @@ +interface llm-template { + + // API request variants + variant api-request { + get-status, + get-history, + clear-history, + message(custom-message), + } + + // Custom message request + record custom-message { + message-type: string, + content: string, + } + + // API response variants + variant api-response { + status(state-overview), + history(list), + clear-history(success-response), + message(success-response), + api-error(error-response) + } + + // Status report of the system + record state-overview { + connected-clients: u64, + message-count: u64, + message-counts-by-channel: list>, + } + + + record success-response { + message: string, + } + + record error-response { + code: u16, + message: string, + } + + record send-request { + target: string, + message: string, + } + + record sample-test-app-message { + author: string, + content: string, + } + + + // Message log structure + record message-log { + source: string, + channel: message-channel, + message-type: message-type, + content: option, + timestamp: u64, + } + + // Message channel types + enum message-channel { + websocket, + http-api, + internal, + external, + timer, + terminal, + } + + // Message type variants + variant message-type { + websocket-open, + websocket-close, + websocket-push-a, + websocket-push-b, + http-get, + http-post, + timer-tick, + local-request, + remote-request, + response-received, + terminal-command, + other(string), + } + + // Hyper API request variants + variant hyper-api-request { + get-status, + get-history, + clear-history, + message(custom-message), + } + + // Hyper API response variants + variant hyper-api-response { + status(state-overview), + history(list), + clear-history(success-response), + message(success-response), + api-error(error-response) + } +} + +world llm-template-dot-os-v0 { + import llm-template; + include process-v1; +} diff --git a/src/new/templates/rust/ui/llm-template/llm-template/Cargo.toml b/src/new/templates/rust/ui/llm-template/llm-template/Cargo.toml new file mode 100644 index 00000000..aef53124 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "llm-template" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +hyperware_process_lib = { version = "1.0.4", features = ["logging"] } +process_macros = "0.1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +wit-bindgen = "0.36.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "hyperware:process" diff --git a/src/new/templates/rust/ui/llm-template/llm-template/src/lib.rs b/src/new/templates/rust/ui/llm-template/llm-template/src/lib.rs new file mode 100644 index 00000000..a345d602 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/src/lib.rs @@ -0,0 +1,203 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use hyperware_process_lib::logging::{error, info, init_logging, Level}; +use hyperware_process_lib::{ + kiprintln, + await_message, call_init, + http::server::{ + HttpBindingConfig, HttpServer, + WsBindingConfig, + }, + Address, Message +}; + +mod types; +use types::{AppConfig, AppState}; +use crate::hyperware::process::llm_template::{MessageChannel, MessageType, MessageLog}; +mod message_handlers; +use message_handlers::*; + +wit_bindgen::generate!({ + path: "target/wit", + world: "llm-template-dot-os-v0", + generate_unused_types: true, + additional_derives: [serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); + +const WS_PATH: &str = "/"; +fn bind_http_endpoints(server: &mut HttpServer) { + let public_config = HttpBindingConfig::new(false, false, false, None); + let authenticated_config = HttpBindingConfig::new(true, false, false, None); + + // Define API paths + let public_paths = vec![ + "/api", // Base API path + "/api/status", // GET status endpoint + "/api/history", // GET history endpoint + ]; + + // Bind public paths + for path in public_paths { + server.bind_http_path(path, public_config.clone()) + .expect(&format!("failed to bind HTTP API path: {}", path)); + } + + // Bind authenticated paths (if needed) + let authenticated_paths = vec![ + "/api/clear-history", // For authenticated clear history requests + ]; + + for path in authenticated_paths { + server.bind_http_path(path, authenticated_config.clone()) + .expect(&format!("failed to bind authenticated HTTP API path: {}", path)); + } +} + +fn get_timestamp() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() +} + +// Helper function to log a message and update counts +fn log_message( + state: &mut AppState, + source: String, + channel: MessageChannel, + message_type: MessageType, + content: Option, +) { + // Add to message history + state.message_history.push(MessageLog { + source, + channel: channel.clone(), + message_type, + content: if state.config.log_content { content } else { None }, + timestamp: get_timestamp(), + }); + + // Update message count for this channel + state.increment_channel_count(channel); + + // Trim history if needed + if state.message_history.len() > state.config.max_history { + state.message_history.remove(0); + } +} + +fn handle_message( + message: &Message, + our: &Address, + state: &mut AppState, + server: &mut HttpServer, +) -> anyhow::Result<()> { + println!("got message: {:?}", message); + kiprintln!("got message: {:?}", message.source()); + + match message.source() { + // Handling HTTP and WS requests + source if source == &make_http_address(our) => { + println!("got http request"); + handle_http_server_request(our, message.body(), state, server) + } + // Handling timer messages (for time-based events) + source if source == &make_timer_address(our) => { + kiprintln!("got timer message"); + handle_timer_message(message.body(), state, server) + } + // Handling internal messages (messages from other processes on the same node) + source if source.node == our.node && source != &make_http_address(our) => { + kiprintln!("got internal message"); + handle_internal_message(source, message.body(), state, server) + } + // Handling terminal messages (for debugging purposes) + source if source == &make_terminal_address(our) => { + kiprintln!("got terminal message"); + handle_terminal_message(message.body(), state, server) + } + // Handling external messages (messages from other applications/hyperdrives) + source => { + kiprintln!("got external message"); + log_message( + state, + "System".to_string(), + MessageChannel::External, + MessageType::Other("External message".to_string()), + Some(format!("got external message from: {:?}", source)), + ); + handle_external_message( + source, + message.body(), + state, + server + ) + }, + } +} + +call_init!(init); +fn init(our: Address) { + // Arguments: file level, terminal level, remote, terminal_level_mapping, max_log_file_size + init_logging(Level::DEBUG, Level::INFO, None, None, None).unwrap(); + info!("begin"); + + // Initialize application state + let mut state = AppState::default(); + state.config = AppConfig { + max_history: 100, + log_content: true, + }; + + let mut server = HttpServer::new(5); + let http_config = HttpBindingConfig::default(); + bind_http_endpoints(&mut server); + + // Bind UI files to routes with index.html at "/"; WS to "/" + server + .serve_ui("ui", vec!["/"], http_config.clone()) + .expect("failed to serve UI"); + + server + .bind_ws_path(WS_PATH, WsBindingConfig::default()) + .expect("failed to bind WS API"); + + // Log initialization + log_message( + &mut state, + "System".to_string(), + MessageChannel::Internal, + MessageType::Other("Initialization".to_string()), + Some("Application started".to_string()), + ); + + loop { + match await_message() { + Err(send_error) => { + error!("got SendError: {send_error}"); + log_message( + &mut state, + "System".to_string(), + MessageChannel::Internal, + MessageType::Other("Error".to_string()), + Some(format!("SendError: {}", send_error)), + ); + }, + Ok(ref message) => { + match handle_message(message, &our, &mut state, &mut server) { + Ok(_) => {} + Err(e) => { + error!("got error while handling message: {e:?}"); + log_message( + &mut state, + "System".to_string(), + MessageChannel::Internal, + MessageType::Other("Error".to_string()), + Some(format!("Error handling message: {}", e)), + ); + } + } + } + } + } +} diff --git a/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_http.rs b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_http.rs new file mode 100644 index 00000000..60d7c53d --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_http.rs @@ -0,0 +1,323 @@ +use hyperware_process_lib::{ + get_blob, Address, + http::server::{ + send_response, HttpServer, HttpServerRequest, StatusCode, send_ws_push, WsMessageType + }, + http::Method, + logging::info, + LazyLoadBlob, last_blob, +}; +use anyhow::anyhow; +use crate::hyperware::process::llm_template::{ + ApiRequest, ApiResponse, StateOverview, SuccessResponse, ErrorResponse, MessageChannel, MessageType +}; +use crate::types::AppState; +use crate::log_message; + +pub fn handle_http_server_request( + _our: &Address, + body: &[u8], + state: &mut AppState, + server: &mut HttpServer, +) -> anyhow::Result<()> { + let Ok(request) = serde_json::from_slice::(body) else { + // Fail quietly if we can't parse the request + info!("couldn't parse message from http_server: {body:?}"); + return Ok(()); + }; + + match request { + HttpServerRequest::WebSocketOpen { + ref path, + channel_id, + } => { + // Track the new websocket connection + state.add_client(channel_id, path.clone()); + + // Log the connection + log_message( + state, + format!("WebSocket:{}", channel_id), + MessageChannel::Websocket, + MessageType::WebsocketOpen, + Some(format!("Path: {}", path)), + ); + + server.handle_websocket_open(path, channel_id); + Ok(()) + }, + HttpServerRequest::WebSocketClose(channel_id) => { + // Log the disconnection + log_message( + state, + format!("WebSocket:{}", channel_id), + MessageChannel::Websocket, + MessageType::WebsocketClose, + Some(format!("Channel ID: {}", channel_id)), + ); + + // Remove the closed connection + state.remove_client(channel_id); + server.handle_websocket_close(channel_id); + Ok(()) + }, + HttpServerRequest::WebSocketPush {channel_id, .. } => { + handle_websocket_push(state, channel_id) + }, + HttpServerRequest::Http(http_request) => { + // Get the HTTP method and path + let Ok(method) = http_request.method() else { + return Err(anyhow!("HTTP request with no method")); + }; + + let path = http_request.path().unwrap_or_default(); + println!("HTTP Request: {} {}", method, path); + info!("HTTP Request: {} {}", method, path); + + // Handle different HTTP methods + match method { + Method::GET => { + // Handle GET requests based on path + match path.as_str() { + "/api/status" => { + // Convert message counts to WIT format + let counts_by_channel: Vec<(String, u64)> = state.message_counts + .iter() + .map(|(k, v)| (format!("{:?}", k), *v as u64)) + .collect(); + + let response = ApiResponse::Status(StateOverview { + connected_clients: state.connected_clients.len() as u64, + message_count: state.message_history.len() as u64, + message_counts_by_channel: counts_by_channel, + }); + + log_message( + state, + "HTTP:GET".to_string(), + MessageChannel::HttpApi, + MessageType::HttpGet, + Some("Status request".to_string()), + ); + + send_response(StatusCode::OK, None, serde_json::to_vec(&response)?); + }, + "/api/history" => { + // Use MessageLog type directly since it's WIT-compatible + let data = state.message_history.clone(); + let response = ApiResponse::History(data); + + log_message( + state, + "HTTP:GET".to_string(), + MessageChannel::HttpApi, + MessageType::HttpGet, + Some("History request".to_string()), + ); + + send_response(StatusCode::OK, None, serde_json::to_vec(&response)?); + }, + _ => { + println!("Non-API path: {}", path); + } + } + }, + Method::POST => { + // For POST requests, we need to parse the body + let Some(blob) = last_blob() else { + let error_response = ApiResponse::ApiError(ErrorResponse { + code: 400, + message: "No request body".to_string(), + }); + send_response(StatusCode::BAD_REQUEST, None, serde_json::to_vec(&error_response)?); + return Ok(()); + }; + + let Ok(request_str) = std::str::from_utf8(&blob.bytes()) else { + let error_response = ApiResponse::ApiError(ErrorResponse { + code: 400, + message: "Invalid UTF-8 in request body".to_string(), + }); + send_response(StatusCode::BAD_REQUEST, None, serde_json::to_vec(&error_response)?); + return Ok(()); + }; + + info!("Request body: {}", request_str); + + // Try to parse the API request + match serde_json::from_str::(request_str) { + Ok(api_request) => { + match api_request { + ApiRequest::ClearHistory => { + // Clear the history + state.message_history.clear(); + state.clear_counts(); + + log_message( + state, + "HTTP:POST".to_string(), + MessageChannel::HttpApi, + MessageType::HttpPost, + Some("History cleared".to_string()), + ); + + let response = ApiResponse::ClearHistory(SuccessResponse { + message: "History cleared successfully".to_string(), + }); + + send_response(StatusCode::OK, None, serde_json::to_vec(&response)?); + }, + ApiRequest::Message(msg) => { + // Log a custom message + log_message( + state, + "HTTP:Custom".to_string(), + MessageChannel::HttpApi, + MessageType::Other(msg.message_type.clone()), + Some(msg.content.clone()), + ); + + let response = ApiResponse::Message(SuccessResponse { + message: "Custom message logged successfully".to_string(), + }); + + send_response(StatusCode::OK, None, serde_json::to_vec(&response)?); + }, + _ => { + // Invalid request - should use GET endpoints instead + let error_response = ApiResponse::ApiError(ErrorResponse { + code: 400, + message: "Invalid request type. Use GET endpoints for status and history.".to_string(), + }); + send_response(StatusCode::BAD_REQUEST, None, serde_json::to_vec(&error_response)?); + } + } + }, + Err(err) => { + let error_response = ApiResponse::ApiError(ErrorResponse { + code: 400, + message: format!("Invalid request format: {}", err), + }); + send_response(StatusCode::BAD_REQUEST, None, serde_json::to_vec(&error_response)?); + } + } + }, + _ => { + let error_response = ApiResponse::ApiError(ErrorResponse { + code: 405, + message: "Method not allowed".to_string(), + }); + send_response(StatusCode::METHOD_NOT_ALLOWED, None, serde_json::to_vec(&error_response)?); + } + } + Ok(()) + } + } +} + +fn handle_websocket_push(state: &mut AppState, channel_id: u32) -> anyhow::Result<()> { + let Some(blob) = get_blob() else { + return Ok(()); + }; + + // Log the message + let content = if let Ok(str) = std::str::from_utf8(&blob.bytes()) { + Some(str.to_string()) + } else { + Some(format!("Binary data: {} bytes", blob.bytes().len())) + }; + + // Process the websocket message + if let Ok(message_str) = std::str::from_utf8(&blob.bytes()) { + if let Ok(api_request) = serde_json::from_str::(message_str) { + match api_request { + ApiRequest::ClearHistory => { + // Clear the history + state.message_history.clear(); + state.clear_counts(); + + // Log the action + log_message( + state, + "WebSocket:Clear".to_string(), + MessageChannel::Websocket, + MessageType::WebsocketPushA, + Some("History cleared".to_string()), + ); + + // Create a success response + let response = ApiResponse::ClearHistory(SuccessResponse { + message: "History cleared successfully".to_string(), + }); + + // Convert to JSON and send only to the requesting client + if let Ok(response_json) = serde_json::to_string(&response) { + info!("Sending WS response to client {}: {}", channel_id, response_json); + + send_ws_push( + channel_id, + WsMessageType::Text, + LazyLoadBlob { + mime: Some("application/json".to_string()), + bytes: response_json.as_bytes().to_vec(), + }, + ); + } + }, + _ => { + // Handle all other request types + let response = match api_request { + ApiRequest::GetStatus => { + // Convert message counts to WIT format + let counts_by_channel: Vec<(String, u64)> = state.message_counts + .iter() + .map(|(k, v)| (format!("{:?}", k), *v as u64)) + .collect(); + + ApiResponse::Status(StateOverview { + connected_clients: state.connected_clients.len() as u64, + message_count: state.message_history.len() as u64, + message_counts_by_channel: counts_by_channel, + }) + }, + ApiRequest::GetHistory => { + ApiResponse::History(state.message_history.clone()) + }, + ApiRequest::Message(msg) => { + // Log a custom message type + log_message( + state, + "WebSocket:Custom".to_string(), + MessageChannel::Websocket, + MessageType::WebsocketPushB, + Some(format!("Type: {}, Content: {}", msg.message_type, msg.content)), + ); + + ApiResponse::Message(SuccessResponse { + message: "Custom message logged successfully".to_string(), + }) + }, + // We already handled ClearHistory above + ApiRequest::ClearHistory => unreachable!(), + }; + + // Convert to JSON and send only to the requesting client + if let Ok(response_json) = serde_json::to_string(&response) { + info!("Sending WS response to client {}: {}", channel_id, response_json); + + send_ws_push( + channel_id, + WsMessageType::Text, + LazyLoadBlob { + mime: Some("application/json".to_string()), + bytes: response_json.as_bytes().to_vec(), + }, + ); + } + } + } + } + } + + Ok(()) +} \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_hyperware.rs b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_hyperware.rs new file mode 100644 index 00000000..af87d30f --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/handle_hyperware.rs @@ -0,0 +1,200 @@ +use hyperware_process_lib::{ + Address, Response, + http::server::{HttpServer, WsMessageType, send_ws_push}, + logging::info, + LazyLoadBlob, +}; +use serde_json; +use crate::types::AppState; +use crate::log_message; +use super::make_terminal_address; +use crate::hyperware::process::llm_template::{ + MessageChannel, MessageType, ApiResponse, StateOverview, SuccessResponse +}; +// Timer handler, usually used for time-based events +pub fn handle_timer_message( + _body: &[u8], + state: &mut AppState, + _server: &mut HttpServer, +) -> anyhow::Result<()> { + // Log the timer message + log_message( + state, + "Timer".to_string(), + MessageChannel::Timer, + MessageType::TimerTick, + Some("Timer event received".to_string()), + ); + info!("Received timer message"); + + // Example: Send status updates to all connected websocket clients + // Convert message counts to WIT format + let counts_by_channel: Vec<(String, u64)> = state.message_counts + .iter() + .map(|(k, v)| (format!("{:?}", k), *v as u64)) + .collect(); + + let response = ApiResponse::Status(StateOverview { + connected_clients: state.connected_clients.len() as u64, + message_count: state.message_history.len() as u64, + message_counts_by_channel: counts_by_channel, + }); + + if let Ok(status_json) = serde_json::to_string(&response) { + for (client_id, _) in &state.connected_clients { + // Sending WS push to all connected clients + send_ws_push( + *client_id, + WsMessageType::Text, + LazyLoadBlob { + mime: Some("application/json".to_string()), + bytes: status_json.as_bytes().to_vec(), + }, + ); + } + } + + Ok(()) +} + +// Terminal handler, usually used for debugging purposes, state checking +pub fn handle_terminal_message( + body: &[u8], + state: &mut AppState, + _server: &mut HttpServer, +) -> anyhow::Result<()> { + // Log the terminal message + let content = if let Ok(str) = std::str::from_utf8(body) { + Some(str.to_string()) + } else { + Some(format!("Binary data: {} bytes", body.len())) + }; + + log_message( + state, + "Terminal".to_string(), + MessageChannel::Terminal, + MessageType::TerminalCommand, + content, + ); + + // Return success response + let response = ApiResponse::Message(SuccessResponse { + message: "Terminal message logged successfully".to_string(), + }); + + Response::new() + .body(serde_json::to_vec(&response)?) + .send()?; + + Ok(()) +} + +pub fn handle_internal_message( + source: &Address, + body: &[u8], + state: &mut AppState, + server: &mut HttpServer, +) -> anyhow::Result<()> { + // Terminal messages are usually used for debugging purposes (checking state, etc.) + if source == &make_terminal_address(source) { + return handle_terminal_message(body, state, server); + } + + // Log the internal message + let content = if let Ok(str) = std::str::from_utf8(body) { + Some(str.to_string()) + } else { + Some(format!("Binary data: {} bytes", body.len())) + }; + + log_message( + state, + format!("Internal:{}", source), + MessageChannel::Internal, + MessageType::LocalRequest, + content, + ); + + // Return success response + let response = ApiResponse::Message(SuccessResponse { + message: "Message logged successfully".to_string(), + }); + + Response::new() + .body(serde_json::to_vec(&response)?) + .send()?; + + Ok(()) +} + +pub fn handle_external_message( + source: &Address, + body: &[u8], + state: &mut AppState, + _server: &mut HttpServer, +) -> anyhow::Result<()> { + // Try to parse the incoming message as a hyper-api-request + let hyper_request: Result = serde_json::from_slice(body); + + let response = match hyper_request { + Ok(request) => { + match request { + HyperApiRequest::GetStatus => { + HyperApiResponse::Status(StateOverview { + connected_clients: state.connected_clients, + message_count: state.message_count, + message_counts_by_channel: state.message_counts_by_channel.clone(), + }) + }, + HyperApiRequest::GetHistory => { + HyperApiResponse::History(state.message_history.clone()) + }, + HyperApiRequest::ClearHistory => { + state.message_history.clear(); + HyperApiResponse::ClearHistory(SuccessResponse { + message: "History cleared successfully".to_string(), + }) + }, + HyperApiRequest::Message(msg) => { + log_message( + state, + format!("External:{}", source), + MessageChannel::External, + MessageType::Other(msg.message_type.clone()), + Some(msg.content.clone()), + ); + HyperApiResponse::Message(SuccessResponse { + message: "Message processed successfully".to_string(), + }) + } + } + }, + Err(_) => { + // Fall back to the original behavior for non-hyper messages + let content = if let Ok(str) = std::str::from_utf8(body) { + Some(str.to_string()) + } else { + Some(format!("Binary data: {} bytes", body.len())) + }; + + log_message( + state, + format!("External:{}", source), + MessageChannel::External, + MessageType::ResponseReceived, + content, + ); + HyperApiResponse::ApiError(ErrorResponse { + code: 400, + message: "Invalid request format".to_string(), + }) + } + }; + + Response::new() + .body(serde_json::to_vec(&response)?) + .send()?; + + Ok(()) +} \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/mod.rs b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/mod.rs new file mode 100644 index 00000000..8aad1ef5 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/src/message_handlers/mod.rs @@ -0,0 +1,25 @@ +mod handle_http; +mod handle_hyperware; + +// Re-export the functions +pub use handle_http::handle_http_server_request; +pub use handle_hyperware::{ + handle_timer_message, handle_terminal_message, + handle_internal_message, handle_external_message +}; + +#[allow(unused_imports)] +use hyperware_process_lib::Address; + +// Helper functions for address creation +pub fn make_http_address(our: &Address) -> Address { + Address::from((our.node(), "http-server", "distro", "sys")) +} + +pub fn make_timer_address(our: &Address) -> Address { + Address::from((our.node(), "timer", "distro", "sys")) +} + +pub fn make_terminal_address(our: &Address) -> Address { + Address::from((our.node(), "terminal", "distro", "sys")) +} \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/llm-template/src/types.rs b/src/new/templates/rust/ui/llm-template/llm-template/src/types.rs new file mode 100644 index 00000000..12301913 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/llm-template/src/types.rs @@ -0,0 +1,66 @@ +use crate::hyperware::process::llm_template::{MessageLog, MessageChannel, MessageType}; + +/// Represents the application state +#[derive(Debug, Clone, Default)] +pub struct AppState { + /// Tracks message history for all channels + pub message_history: Vec, + /// Message counts by channel + pub message_counts: Vec<(MessageChannel, usize)>, + /// Configuration settings + pub config: AppConfig, + /// Connected WebSocket clients (channel_id -> path) + pub connected_clients: Vec<(u32, String)>, +} + +/// Configuration for the application +#[derive(Debug, Clone)] +pub struct AppConfig { + /// Maximum number of messages to keep in history + pub max_history: usize, + /// Whether to log message content + pub log_content: bool, +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + max_history: 100, + log_content: true, + } + } +} + +impl AppState { + /// Increment count for a channel + pub fn increment_channel_count(&mut self, channel: MessageChannel) { + if let Some(count) = self.message_counts.iter_mut().find(|(ch, _)| *ch == channel) { + count.1 += 1; + } else { + self.message_counts.push((channel, 1)); + } + } + + /// Add a client connection + pub fn add_client(&mut self, channel_id: u32, path: String) { + self.connected_clients.push((channel_id, path)); + } + + /// Remove a client connection + pub fn remove_client(&mut self, channel_id: u32) { + self.connected_clients.retain(|(id, _)| *id != channel_id); + } + + /// Get client path + pub fn get_client_path(&self, channel_id: u32) -> Option<&str> { + self.connected_clients + .iter() + .find(|(id, _)| *id == channel_id) + .map(|(_, path)| path.as_str()) + } + + /// Clear message counts + pub fn clear_counts(&mut self) { + self.message_counts.clear(); + } +} \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/metadata.json b/src/new/templates/rust/ui/llm-template/metadata.json new file mode 100644 index 00000000..da0ac008 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/metadata.json @@ -0,0 +1,18 @@ +{ + "name": "llm-template", + "description": "", + "image": "", + "properties": { + "package_name": "llm-template", + "current_version": "0.1.0", + "publisher": "template.os", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 1, + "dependencies": [] + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/rust/ui/llm-template/pkg/manifest.json b/src/new/templates/rust/ui/llm-template/pkg/manifest.json new file mode 100644 index 00000000..19cbc90b --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/pkg/manifest.json @@ -0,0 +1,15 @@ +[ + { + "process_name": "llm-template", + "process_wasm_path": "/llm-template.wasm", + "on_exit": "Restart", + "request_networking": true, + "request_capabilities": [ + "http-server:distro:sys", + "vfs:distro:sys", + "timer:distro:sys" + ], + "grant_capabilities": [], + "public": true + } +] diff --git a/src/new/templates/rust/ui/llm-template/resources/frontendSetup.md b/src/new/templates/rust/ui/llm-template/resources/frontendSetup.md new file mode 100644 index 00000000..57824231 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/resources/frontendSetup.md @@ -0,0 +1,110 @@ +# Frontend/UI Development + +Hyperware can easily serve any webpage or web app developed with normal libraries and frameworks. + +There are some specific endpoints, JS libraries, and `process_lib` functions that are helpful for doing frontend development. + +There are also some important considerations and "gotchas" that can happen when trying to do frontend development. + +Hyperware can serve a website or web app just like any HTTP webserver. +The preferred method is to upload your static assets on install by placing them in the `pkg` folder. +By convention, `kit` bundles these assets into a directory inside `pkg` called `ui`, but you can call it anything. +You **must** place your `index.html` in the top-level folder. +The structure should look like this: + +``` +my-package +└── pkg + └── ui (can have any name) + ├── assets (can have any name) + └── index.html +``` + +## /our & /our.js + +Every node has both `/our` and `/our.js` endpoints. +`/our` returns the node's ID as a string like `'my-node'`. +`/our.js` returns a JS script that sets `window.our = { node: 'my-node' }`. +By convention, you can then easily set `window.our.process` either in your UI code or from a process-specific endpoint. +The frontend would then have `window.our` set for use in your code. + +## Serving a Website + +The simplest way to serve a UI is using the `serve_ui` function from `process_lib`: + +``` +serve_ui(&our, "ui", true, false, vec!["/"]).unwrap(); +``` + +This will serve the `index.html` in the specified folder (here, `"ui"`) at the home path of your process. +If your process is called `my-process:my-package:template.os` and your Hyperware node is running locally on port 8080, +then the UI will be served at `http://localhost:8080/my-process:my-package:template.os`. + +`serve_ui` takes five arguments: our `&Address`, the name of the folder that contains your frontend, whether the UI requires authentication, whether the UI is local-only, and the path(s) on which to serve the UI (usually `["/"]`). + +## Development without kit + +The `kit` UI template uses the React framework compiled with Vite. +But you can use any UI framework as long as it generates an `index.html` and associated assets. +To make development easy, your setup should support a base URL and http proxying. + +### Base URL + +All processes on Hyperware are namespaced by process name in the standard format of `process:package:publisher`. +So if your process is called `my-process:my-package:template.os`, then your process can only bind HTTP paths that start with `/my-process:my-package:template.os`. +Your UI should be developed and compiled with the base URL set to the appropriate process path. + +#### Vite + +In `vite.config.ts` (or `.js`) set `base` to your full process name, i.e. +``` +base: '/my-process:my-package:template.os' +``` + +#### Create React App + +In `package.json` set `homepage` to your full process name, i.e. +``` +homepage: '/my-process:my-package:template.os' +``` + +### Proxying HTTP Requests + +In UI development, it is very useful to proxy HTTP requests from the in-dev UI to your Hyperware node. +Below are some examples. + +#### Vite + +Follow the `server` entry in the [kit template](https://github.com/hyperware-ai/kit/blob/master/src/new/templates/ui/chat/ui/vite.config.ts#L31-L47) in your own `vite.config.ts`. + +#### Create React App + +In `package.json` set `proxy` to your nodes URL, i.e. +``` +proxy: 'http://localhost:8080' +``` + +### Making HTTP Requests + +When making HTTP requests in your UI, make sure to prepend your base URL to the request. +For example, if your base URL is `/my-process:my-package:template.os`, then a `fetch` request to `/my-endpoint` would look like this: + +``` +fetch('/my-process:my-package:template.os/my-endpoint') +``` + +## Local Development and "gotchas" + +When developing a frontend locally, particularly with a framework like React, it is helpful to proxy HTTP requests through to your node. +The `vite.config.ts` provided in the `kit` template has code to handle this proxying. + +It is important to remember that the frontend will always have the process name as the first part of the HTTP path, +so all HTTP requests and file sources should start with the process name. +Many frontend JavaScript frameworks will handle this by default if you set the `base` or `baseUrl` properly. + +In development, websocket connections can be more annoying to proxy, so it is often easier to simply hardcode the URL if in development. +See your framework documentation for how to check if you are in dev or prod. +The `kit` template already handles this for you. + +Developing against a remote node is simple, you just have to change the proxy target in `vite.config.ts` to the URL of your node. +By default the template will target `http://localhost:8080`. \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/resources/keyValueAPI.md b/src/new/templates/rust/ui/llm-template/resources/keyValueAPI.md new file mode 100644 index 00000000..230e99e1 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/resources/keyValueAPI.md @@ -0,0 +1,199 @@ +#### Creating/Opening a key-value store + +```rust +use hyperware_process_lib::kv; + +let kv = kv::open(our.package_id(), "birthdays")?; + +// You can now pass this KV struct as a reference to other functions +``` + +#### Set + +```rust +let key = b"hello"; +let value= b"world"; + +let returnvalue = kv.set(&key, &value, None)?; +// The third argument None is for tx_id. +// You can group sets and deletes and commit them later. +``` + +#### Get + +```rust +let key = b"hello"; + +let returnvalue = kv.get(&key)?; +``` + +#### Delete + +```rust +let key = b"hello"; + +kv.delete(&key, None)?; +``` + +#### Transactions + +```rust +let tx_id = kv.begin_tx()?; + +let key = b"hello"; +let key2 = b"deleteme"; +let value= b"value"; + +kv.set(&key, &value, Some(tx_id))?; +kv.delete(&key, Some(tx_id))?; + +kv.commit_tx(tx_id)?; +``` + +### API + +```rust +/// Actions are sent to a specific key value database. `db` is the name, +/// `package_id` is the [`PackageId`] that created the database. Capabilities +/// are checked: you can access another process's database if it has given +/// you the read and/or write capability to do so. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KvRequest { + pub package_id: PackageId, + pub db: String, + pub action: KvAction, +} + +/// IPC Action format representing operations that can be performed on the +/// key-value runtime module. These actions are included in a [`KvRequest`] +/// sent to the `kv:distro:sys` runtime module. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum KvAction { + /// Opens an existing key-value database or creates a new one if it doesn't exist. + /// Requires `package_id` in [`KvRequest`] to match the package ID of the sender. + /// The sender will own the database and can remove it with [`KvAction::RemoveDb`]. + /// + /// A successful open will respond with [`KvResponse::Ok`]. Any error will be + /// contained in the [`KvResponse::Err`] variant. + Open, + /// Permanently deletes the entire key-value database. + /// Requires `package_id` in [`KvRequest`] to match the package ID of the sender. + /// Only the owner can remove the database. + /// + /// A successful remove will respond with [`KvResponse::Ok`]. Any error will be + /// contained in the [`KvResponse::Err`] variant. + RemoveDb, + /// Sets a value for the specified key in the database. + /// + /// # Parameters + /// * `key` - The key as a byte vector + /// * `tx_id` - Optional transaction ID if this operation is part of a transaction + /// * blob: [`Vec`] - Byte vector to store for the key + /// + /// Using this action requires the sender to have the write capability + /// for the database. + /// + /// A successful set will respond with [`KvResponse::Ok`]. Any error will be + /// contained in the [`KvResponse::Err`] variant. + Set { key: Vec, tx_id: Option }, + /// Deletes a key-value pair from the database. + /// + /// # Parameters + /// * `key` - The key to delete as a byte vector + /// * `tx_id` - Optional transaction ID if this operation is part of a transaction + /// + /// Using this action requires the sender to have the write capability + /// for the database. + /// + /// A successful delete will respond with [`KvResponse::Ok`]. Any error will be + /// contained in the [`KvResponse::Err`] variant. + Delete { key: Vec, tx_id: Option }, + /// Retrieves the value associated with the specified key. + /// + /// # Parameters + /// * The key to look up as a byte vector + /// + /// Using this action requires the sender to have the read capability + /// for the database. + /// + /// A successful get will respond with [`KvResponse::Get`], where the response blob + /// contains the value associated with the key if any. Any error will be + /// contained in the [`KvResponse::Err`] variant. + Get(Vec), + /// Begins a new transaction for atomic operations. + /// + /// Sending this will prompt a [`KvResponse::BeginTx`] response with the + /// transaction ID. Any error will be contained in the [`KvResponse::Err`] variant. + BeginTx, + /// Commits all operations in the specified transaction. + /// + /// # Parameters + /// * `tx_id` - The ID of the transaction to commit + /// + /// A successful commit will respond with [`KvResponse::Ok`]. Any error will be + /// contained in the [`KvResponse::Err`] variant. + Commit { tx_id: u64 }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum KvResponse { + /// Indicates successful completion of an operation. + /// Sent in response to actions Open, RemoveDb, Set, Delete, and Commit. + Ok, + /// Returns the transaction ID for a newly created transaction. + /// + /// # Fields + /// * `tx_id` - The ID of the newly created transaction + BeginTx { tx_id: u64 }, + /// Returns the value for the key that was retrieved from the database. + /// + /// # Parameters + /// * The retrieved key as a byte vector + /// * blob: [`Vec`] - Byte vector associated with the key + Get(Vec), + /// Indicates an error occurred during the operation. + Err(KvError), +} + +#[derive(Clone, Debug, Serialize, Deserialize, Error)] +pub enum KvError { + #[error("db [{0}, {1}] does not exist")] + NoDb(PackageId, String), + #[error("key not found")] + KeyNotFound, + #[error("no transaction {0} found")] + NoTx(u64), + #[error("no write capability for requested DB")] + NoWriteCap, + #[error("no read capability for requested DB")] + NoReadCap, + #[error("request to open or remove DB with mismatching package ID")] + MismatchingPackageId, + #[error("failed to generate capability for new DB")] + AddCapFailed, + #[error("kv got a malformed request that either failed to deserialize or was missing a required blob")] + MalformedRequest, + #[error("RocksDB internal error: {0}")] + RocksDBError(String), + #[error("IO error: {0}")] + IOError(String), +} + +/// The JSON parameters contained in all capabilities issued by `kv:distro:sys`. +/// +/// # Fields +/// * `kind` - The kind of capability, either [`KvCapabilityKind::Read`] or [`KvCapabilityKind::Write`] +/// * `db_key` - The database key, a tuple of the [`PackageId`] that created the database and the database name +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KvCapabilityParams { + pub kind: KvCapabilityKind, + pub db_key: (PackageId, String), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum KvCapabilityKind { + Read, + Write, +} +``` \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/resources/sqlite.md b/src/new/templates/rust/ui/llm-template/resources/sqlite.md new file mode 100644 index 00000000..669023af --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/resources/sqlite.md @@ -0,0 +1,216 @@ +#### Creating/Opening a SQLite database + +```rust +use hyperware_process_lib::sqlite; + +let db = sqlite::open(our.package_id(), "users")?; +// You can now pass this SQLite struct as a reference to other functions +``` + +#### Write + +```rust +let statement = "INSERT INTO users (name) VALUES (?), (?), (?);".to_string(); +let params = vec![ +serde_json::Value::String("Bob".to_string()), +serde_json::Value::String("Charlie".to_string()), +serde_json::Value::String("Dave".to_string()), +]; + +sqlite.write(statement, params, None)?; +``` + +#### Read + +```rust +let query = "SELECT FROM users;".to_string(); +let rows = sqlite.read(query, vec![])?; +// rows: Vec> +println!("rows: {}", rows.len()); +for row in rows { + println!(row.get("name")); +} +``` + +#### Transactions + +```rust +let tx_id = sqlite.begin_tx()?; + +let statement = "INSERT INTO users (name) VALUES (?);".to_string(); +let params = vec![serde_json::Value::String("Eve".to_string())]; +let params2 = vec![serde_json::Value::String("Steve".to_string())]; + +sqlite.write(statement, params, Some(tx_id))?; +sqlite.write(statement, params2, Some(tx_id))?; + +sqlite.commit_tx(tx_id)?; +``` + +### API + +```rust +/// Actions are sent to a specific SQLite database. `db` is the name, +/// `package_id` is the [`PackageId`] that created the database. Capabilities +/// are checked: you can access another process's database if it has given +/// you the read and/or write capability to do so. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SqliteRequest { + pub package_id: PackageId, + pub db: String, + pub action: SqliteAction, +} + +/// IPC Action format representing operations that can be performed on the +/// SQLite runtime module. These actions are included in a [`SqliteRequest`] +/// sent to the `sqlite:distro:sys` runtime module. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum SqliteAction { + /// Opens an existing key-value database or creates a new one if it doesn't exist. + /// Requires `package_id` in [`SqliteRequest`] to match the package ID of the sender. + /// The sender will own the database and can remove it with [`SqliteAction::RemoveDb`]. + /// + /// A successful open will respond with [`SqliteResponse::Ok`]. Any error will be + /// contained in the [`SqliteResponse::Err`] variant. + Open, + /// Permanently deletes the entire key-value database. + /// Requires `package_id` in [`SqliteRequest`] to match the package ID of the sender. + /// Only the owner can remove the database. + /// + /// A successful remove will respond with [`SqliteResponse::Ok`]. Any error will be + /// contained in the [`SqliteResponse::Err`] variant. + RemoveDb, + /// Executes a write statement (INSERT/UPDATE/DELETE) + /// + /// * `statement` - SQL statement to execute + /// * `tx_id` - Optional transaction ID + /// * blob: Vec - Parameters for the SQL statement, where SqlValue can be: + /// - null + /// - boolean + /// - i64 + /// - f64 + /// - String + /// - Vec (binary data) + /// + /// Using this action requires the sender to have the write capability + /// for the database. + /// + /// A successful write will respond with [`SqliteResponse::Ok`]. Any error will be + /// contained in the [`SqliteResponse::Err`] variant. + Write { + statement: String, + tx_id: Option, + }, + /// Executes a read query (SELECT) + /// + /// * blob: Vec - Parameters for the SQL query, where SqlValue can be: + /// - null + /// - boolean + /// - i64 + /// - f64 + /// - String + /// - Vec (binary data) + /// + /// Using this action requires the sender to have the read capability + /// for the database. + /// + /// A successful query will respond with [`SqliteResponse::Query`], where the + /// response blob contains the results of the query. Any error will be contained + /// in the [`SqliteResponse::Err`] variant. + Query(String), + /// Begins a new transaction for atomic operations. + /// + /// Sending this will prompt a [`SqliteResponse::BeginTx`] response with the + /// transaction ID. Any error will be contained in the [`SqliteResponse::Err`] variant. + BeginTx, + /// Commits all operations in the specified transaction. + /// + /// # Parameters + /// * `tx_id` - The ID of the transaction to commit + /// + /// A successful commit will respond with [`SqliteResponse::Ok`]. Any error will be + /// contained in the [`SqliteResponse::Err`] variant. + Commit { tx_id: u64 }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum SqliteResponse { + /// Indicates successful completion of an operation. + /// Sent in response to actions Open, RemoveDb, Write, Query, BeginTx, and Commit. + Ok, + /// Returns the results of a query. + /// + /// * blob: Vec> - Array of rows, where each row contains SqlValue types: + /// - null + /// - boolean + /// - i64 + /// - f64 + /// - String + /// - Vec (binary data) + Read, + /// Returns the transaction ID for a newly created transaction. + /// + /// # Fields + /// * `tx_id` - The ID of the newly created transaction + BeginTx { tx_id: u64 }, + /// Indicates an error occurred during the operation. + Err(SqliteError), +} + +/// Used in blobs to represent array row values in SQLite. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum SqlValue { + Integer(i64), + Real(f64), + Text(String), + Blob(Vec), + Boolean(bool), + Null, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Error)] +pub enum SqliteError { + #[error("db [{0}, {1}] does not exist")] + NoDb(PackageId, String), + #[error("no transaction {0} found")] + NoTx(u64), + #[error("no write capability for requested DB")] + NoWriteCap, + #[error("no read capability for requested DB")] + NoReadCap, + #[error("request to open or remove DB with mismatching package ID")] + MismatchingPackageId, + #[error("failed to generate capability for new DB")] + AddCapFailed, + #[error("write statement started with non-existent write keyword")] + NotAWriteKeyword, + #[error("read query started with non-existent read keyword")] + NotAReadKeyword, + #[error("parameters blob in read/write was misshapen or contained invalid JSON objects")] + InvalidParameters, + #[error("sqlite got a malformed request that failed to deserialize")] + MalformedRequest, + #[error("rusqlite error: {0}")] + RusqliteError(String), + #[error("IO error: {0}")] + IOError(String), +} + +/// The JSON parameters contained in all capabilities issued by `sqlite:distro:sys`. +/// +/// # Fields +/// * `kind` - The kind of capability, either [`SqliteCapabilityKind::Read`] or [`SqliteCapabilityKind::Write`] +/// * `db_key` - The database key, a tuple of the [`PackageId`] that created the database and the database name +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SqliteCapabilityParams { + pub kind: SqliteCapabilityKind, + pub db_key: (PackageId, String), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum SqliteCapabilityKind { + Read, + Write, +} +``` \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/resources/vfsAPI.md b/src/new/templates/rust/ui/llm-template/resources/vfsAPI.md new file mode 100644 index 00000000..21534a44 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/resources/vfsAPI.md @@ -0,0 +1,280 @@ +# VFS API + +## Drives + +A drive is a directory within a package's VFS directory, e.g., `app-store:sys/pkg/` or `your_package:publisher.os/my_drive/`. +Drives are owned by packages. +Packages can share access to drives they own via [capabilities](../system/process/capabilities.md). +Each package is spawned with two drives: [`pkg/`](#pkg-drive) and [`tmp/`](#tmp-drive). +All processes in a package have caps to those drives. +Processes can also create additional drives. + +### `pkg/` drive + +The `pkg/` drive contains metadata about the package that Hyperware requires to run that package, `.wasm` binaries, and optionally the API of the package and the UI. +When creating packages, the `pkg/` drive is populated by [`kit build`](../kit/build.md) and loaded into the Hyperware node using [`kit start-package`](../kit/start-package.md). + +### `tmp/` drive + +The `tmp/` drive can be written to directly by the owning package using standard filesystem functionality (i.e. `std::fs` in Rust) via WASI in addition to the Hyperware VFS. + +### Imports + +```rust +use hyperware_process_lib::vfs::{ + create_drive, open_file, open_dir, create_file, metadata, File, Directory, +}; +``` + +### Opening/Creating a Drive + +```rust +let drive_path: String = create_drive(our.package_id(), "drive_name")?; +// you can now prepend this path to any files/directories you're interacting with +let file = open_file(&format!("{}/hello.txt", &drive_path), true); +``` + +### Sharing a Drive Capability + +```rust +let vfs_read_cap = serde_json::json!({ + "kind": "read", + "drive": drive_path, +}).to_string(); + +let vfs_address = Address { + node: our.node.clone(), + process: ProcessId::from_str("vfs:distro:sys").unwrap(), +}; + +// get this capability from our store +let cap = get_capability(&vfs_address, &vfs_read_cap); + +// now if we have that Capability, we can attach it to a subsequent message. +if let Some(cap) = cap { + Request::new() + .capabilities(vec![cap]) + .body(b"hello".to_vec()) + .send()?; +} +``` + +```rust +// the receiving process can then save the capability to it's store, and open the drive. +save_capabilities(incoming_request.capabilities); +let dir = open_dir(&drive_path, false)?; +``` + +### Files + +#### Open a File + +```rust +/// Opens a file at path, if no file at path, creates one if boolean create is true. +let file_path = format!("{}/hello.txt", &drive_path); +let file = open_file(&file_path, true); +``` + +#### Create a File + +```rust +/// Creates a file at path, if file found at path, truncates it to 0. +let file_path = format!("{}/hello.txt", &drive_path); +let file = create_file(&file_path); +``` + +#### Read a File + +```rust +/// Reads the entire file, from start position. +/// Returns a vector of bytes. +let contents = file.read()?; +``` + +#### Write a File + +```rust +/// Write entire slice as the new file. +/// Truncates anything that existed at path before. +let buffer = b"Hello!"; +file.write(&buffer)?; +``` + +#### Write to File + +```rust +/// Write buffer to file at current position, overwriting any existing data. +let buffer = b"World!"; +file.write_all(&buffer)?; +``` + +#### Read at position + +```rust +/// Read into buffer from current cursor position +/// Returns the amount of bytes read. +let mut buffer = vec![0; 5]; +file.read_at(&buffer)?; +``` + +#### Set Length + +```rust +/// Set file length, if given size > underlying file, fills it with 0s. +file.set_len(42)?; +``` + +#### Seek to a position + +```rust +/// Seek file to position. +/// Returns the new position. +let position = SeekFrom::End(0); +file.seek(&position)?; +``` + +#### Sync + +```rust +/// Syncs path file buffers to disk. +file.sync_all()?; +``` + +#### Metadata + +```rust +/// Metadata of a path, returns file type and length. +let metadata = file.metadata()?; +``` + +### Directories + +#### Open a Directory + +```rust +/// Opens or creates a directory at path. +/// If trying to create an existing file, will just give you the path. +let dir_path = format!("{}/my_pics", &drive_path); +let dir = open_dir(&dir_path, true); +``` + +#### Read a Directory + +```rust +/// Iterates through children of directory, returning a vector of DirEntries. +/// DirEntries contain the path and file type of each child. +let entries = dir.read()?; +``` + +#### General path Metadata + +```rust +/// Metadata of a path, returns file type and length. +let some_path = format!("{}/test", &drive_path); +let metadata = metadata(&some_path)?; +``` + +### API + +```rust +/// IPC Request format for the vfs:distro:sys runtime module. +#[derive(Debug, Serialize, Deserialize)] +pub struct VfsRequest { + pub path: String, + pub action: VfsAction, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub enum VfsAction { + CreateDrive, + CreateDir, + CreateDirAll, + CreateFile, + OpenFile { create: bool }, + CloseFile, + Write, + WriteAll, + Append, + SyncAll, + Read, + ReadDir, + ReadToEnd, + ReadExact { length: u64 }, + ReadToString, + Seek(SeekFrom), + RemoveFile, + RemoveDir, + RemoveDirAll, + Rename { new_path: String }, + Metadata, + AddZip, + CopyFile { new_path: String }, + Len, + SetLen(u64), + Hash, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub enum SeekFrom { + Start(u64), + End(i64), + Current(i64), +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum FileType { + File, + Directory, + Symlink, + Other, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct FileMetadata { + pub file_type: FileType, + pub len: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DirEntry { + pub path: String, + pub file_type: FileType, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum VfsResponse { + Ok, + Err(VfsError), + Read, + SeekFrom { new_offset: u64 }, + ReadDir(Vec), + ReadToString(String), + Metadata(FileMetadata), + Len(u64), + Hash([u8; 32]), +} + +#[derive(Error, Debug, Serialize, Deserialize)] +pub enum VfsError { + #[error("No capability for action {action} at path {path}")] + NoCap { action: String, path: String }, + #[error("Bytes blob required for {action} at path {path}")] + BadBytes { action: String, path: String }, + #[error("bad request error: {error}")] + BadRequest { error: String }, + #[error("error parsing path: {path}: {error}")] + ParseError { error: String, path: String }, + #[error("IO error: {error}, at path {path}")] + IOError { error: String, path: String }, + #[error("kernel capability channel error: {error}")] + CapChannelFail { error: String }, + #[error("Bad JSON blob: {error}")] + BadJson { error: String }, + #[error("File not found at path {path}")] + NotFound { path: String }, + #[error("Creating directory failed at path: {path}: {error}")] + CreateDirError { path: String, error: String }, + #[error("Other error: {error}")] + Other { error: String }, +} +``` \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/Cargo.toml b/src/new/templates/rust/ui/llm-template/test/llm-template-test/Cargo.toml new file mode 100644 index 00000000..8150b415 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] +resolver = "2" +members = [ + "llm-template-test", +] + +[profile.release] +panic = "abort" +opt-level = "s" +lto = true diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/api/llm-template-test:template.os-v0.wit b/src/new/templates/rust/ui/llm-template/test/llm-template-test/api/llm-template-test:template.os-v0.wit new file mode 100644 index 00000000..572bcc47 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/api/llm-template-test:template.os-v0.wit @@ -0,0 +1,5 @@ +world llm-template-test-template-dot-os-v0 { + import llm-template; + import tester; + include process-v1; +} diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/Cargo.toml b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/Cargo.toml new file mode 100644 index 00000000..b2516d8f --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "llm-template-test" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +anyhow = "1.0" +bincode = "1.3" +hyperware_process_lib = "1.0.4" +process_macros = { git = "https://github.com/hyperware-ai/process_macros", rev = "626e501" } +rmp-serde = "1.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +wit-bindgen = "0.24.0" + +[lib] +crate-type = ["cdylib"] + +[package.metadata.component] +package = "hyperware:process" diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/client_ops.rs b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/client_ops.rs new file mode 100644 index 00000000..7fafe811 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/client_ops.rs @@ -0,0 +1,76 @@ +use crate::*; +use hyperware_process_lib::{Address, Request}; +use serde_json::to_vec; +use crate::hyperware::process::llm_template::{HyperApiRequest, HyperApiResponse, CustomMessage}; + +pub fn run_client_ops(log_file: &mut File, client_addresses: &Vec
) -> anyhow::Result<()> { + for client in client_addresses.iter() { + send_client_operation(client, log_file)?; + write_log( + log_file, + &format!( + "Done running client operations for {}, ", client, + ), + )?; + } + write_log(log_file, &format!("Done creating curations"))?; + Ok(()) +} + +fn send_client_operation(client: &Address, log_file: &mut File) -> anyhow::Result<()> { + let get_status_request = HyperApiRequest::GetStatus; + let get_history_request = HyperApiRequest::GetHistory; + let message_request = HyperApiRequest::Message(CustomMessage { + message_type: "test".to_string(), + content: "test message".to_string() + }); + + // Send GetStatus request + let status_request_bytes = to_vec(&get_status_request).unwrap(); + let status_response = Request::to(client.clone()) + .body(status_request_bytes) + .send_and_await_response(10)?? + .body() + .to_vec(); + match serde_json::from_slice::(&status_response) { + Ok(response) => write_log(log_file, &format!("GetStatus response from client {}: {:?}", client, response))?, + Err(e) => { + write_log(log_file, &format!("GetStatus error parsing response from client {}: {:?}", client, e))?; + write_log(log_file, &format!("Raw response: {}", String::from_utf8_lossy(&status_response)))?; + } + } + + // Send GetHistory request + let history_request_bytes = to_vec(&get_history_request).unwrap(); + let history_response = Request::to(client.clone()) + .body(history_request_bytes) + .send_and_await_response(10)?? + .body() + .to_vec(); + match serde_json::from_slice::(&history_response) { + Ok(response) => write_log(log_file, &format!("GetHistory response from client {}: {:?}", client, response))?, + Err(e) => { + write_log(log_file, &format!("GetHistory error parsing response from client {}: {:?}", client, e))?; + write_log(log_file, &format!("Raw response: {}", String::from_utf8_lossy(&history_response)))?; + } + } + + // Send Message request + let message_request_bytes = to_vec(&message_request).unwrap(); + let message_response = Request::to(client.clone()) + .body(message_request_bytes) + .send_and_await_response(10)?? + .body() + .to_vec(); + match serde_json::from_slice::(&message_response) { + Ok(response) => write_log(log_file, &format!("Message response from client {}: {:?}", client, response))?, + Err(e) => { + write_log(log_file, &format!("Message error parsing response from client {}: {:?}", client, e))?; + write_log(log_file, &format!("Raw response: {}", String::from_utf8_lossy(&message_response)))?; + } + } + + write_log(log_file, &format!("All operations completed for client {}", client))?; + + Ok(()) +} \ No newline at end of file diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/lib.rs b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/lib.rs new file mode 100644 index 00000000..46503925 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/lib.rs @@ -0,0 +1,138 @@ +use crate::hyperware::process::tester::{Request as TesterRequest, Response as TesterResponse, RunRequest, FailResponse}; +use hyperware_process_lib::{await_message, call_init, print_to_terminal, println, Address, ProcessId, Request, Response, kiprintln, + http::server::{ + send_response, HttpServer, HttpServerRequest, StatusCode, send_ws_push, WsMessageType, + }, + vfs::{create_drive, create_file, File}, + our, +}; +mod utils; +mod client_ops; +mod tester_lib; + +use utils::*; +use client_ops::*; + +// Add type alias to disambiguate Error +type ConversionError = core::convert::Infallible; + +wit_bindgen::generate!({ + path: "target/wit", + world: "llm-template-test-template-dot-os-v0", + generate_unused_types: true, + additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto], +}); +fn handle_message(log_file: &mut File) -> anyhow::Result<()> { + kiprintln!("handle_message called"); + + match run_tests(log_file) { + Ok(_) => { + kiprintln!("Tests completed successfully"); + write_log(log_file, "Tests completed successfully")?; + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + kiprintln!("Sent success response"); + }, + Err(e) => { + kiprintln!("Error running tests: {:?}", e); + } + } + + kiprintln!("handle_message completed"); + Ok(()) +} +fn init_tests(our: Address) -> anyhow::Result> { + kiprintln!("Init tests called with our address: {}", our); + + let message = match await_message() { + Ok(msg) => msg, + Err(e) => { + kiprintln!("Error awaiting message: {:?}", e); + return Err(anyhow::anyhow!("Error awaiting message: {:?}", e)); + } + }; + + if !message.is_request() { + kiprintln!("Received message is not a request"); + fail!("received-non-request"); + // The fail! macro will panic, so this won't be reached + } + + let source = message.source(); + kiprintln!("Message source: {:?}", source); + + if our.node != source.node { + kiprintln!("Rejecting foreign message from {:?}", source); + return Err(anyhow::anyhow!( + "rejecting foreign Message from {:?}", + source, + )); + } + + let request = match message.body().try_into() { + Ok(TesterRequest::Run(req)) => req, + Err(e) => { + fail!("error-parsing-message-body"); + kiprintln!("Error parsing message body: {:?}", e); + return Err(anyhow::anyhow!("Error parsing message body: {:?}", e)); + } + }; + + let node_names = request.input_node_names; + kiprintln!("node_names: {:?}", node_names); + + if node_names.len() < 2 { + fail!("not-enough-nodes"); + } + + if our.node != node_names[0] { + kiprintln!("We are not the master node. Our: {}, master: {}", our.node, node_names[0]); + // we are not master node: return + Response::new() + .body(TesterResponse::Run(Ok(()))) + .send() + .unwrap(); + return Ok(vec![]); + } + + kiprintln!("We are the master node, proceeding with test"); + Ok(node_names) +} + +fn run_tests(log_file: &mut File) -> anyhow::Result<()> { + let client_node_names = init_tests(our())?; + write_log(log_file, &format!("Found client nodes: {:?}", client_node_names))?; + + if client_node_names.is_empty() { + write_log(log_file, "No client nodes to test, exiting early")?; + return Ok(()); + } + + let client_addresses = get_client_addresses(&client_node_names)?; + write_log(log_file, &format!("Client addresses: {:?}", client_addresses))?; + + write_log(log_file, "----------------------------------------")?; + write_log(log_file, "Starting client operations")?; + run_client_ops(log_file, &client_addresses)?; + write_log(log_file, "----------------------------------------")?; + write_log(log_file, "Done running client operations")?; + + Ok(()) +} + +call_init!(init); +fn init(_our: Address) -> anyhow::Result<()> { + let mut log_file = create_log_file()?; + + loop { + match handle_message(&mut log_file) { + Ok(()) => {}, + Err(e) => { + print_to_terminal(0, format!("sample_test_app_test: error: {e:?}").as_str()); + fail!("sample_test_app_test"); + }, + }; + } +} diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/tester_lib.rs b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/tester_lib.rs new file mode 100644 index 00000000..3fdaef81 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/tester_lib.rs @@ -0,0 +1,30 @@ +#[allow(unused_imports)] +use crate::hyperware::process::tester::{FailResponse, Response as TesterResponse}; + +#[macro_export] +macro_rules! fail { + ($test:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: file!().into(), + line: line!(), + column: column!(), + }))) + .send() + .unwrap(); + panic!("") + }; + ($test:expr, $file:expr, $line:expr, $column:expr) => { + Response::new() + .body(TesterResponse::Run(Err(FailResponse { + test: $test.into(), + file: $file.into(), + line: $line, + column: $column, + }))) + .send() + .unwrap(); + panic!("") + }; +} diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/utils.rs b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/utils.rs new file mode 100644 index 00000000..adcf1520 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/llm-template-test/src/utils.rs @@ -0,0 +1,66 @@ +use crate::*; + +pub fn create_log_file() -> anyhow::Result { + let our = our(); + kiprintln!("Creating log file for {}", our); + + let drive_path = match create_drive(our.package_id(), "test_logs", Some(5)) { + Ok(path) => { + kiprintln!("Created drive at path: {}", path); + path + }, + Err(e) => { + kiprintln!("Failed to create drive: {:?}", e); + return Err(e.into()); + } + }; + + let file_path = format!("{}/llm-template-test.log", drive_path); + kiprintln!("Creating log file at: {}", file_path); + + match create_file(&file_path, None) { + Ok(file) => { + kiprintln!("Log file created successfully"); + Ok(file) + }, + Err(e) => { + kiprintln!("Failed to create log file: {:?}", e); + Err(e.into()) + } + } +} + +pub fn write_log(file: &mut File, msg: &str) -> anyhow::Result<()> { + file.write_all(format!("{}\n", msg).as_bytes())?; + file.sync_all()?; + Ok(()) +} + +pub fn get_client_addresses(node_names: &Vec) -> anyhow::Result> { + kiprintln!("Processing node names: {:?}", node_names); + + let client_addresses: Vec
= node_names + .iter() + .filter(|name| { + // This will match "client0.os", "client1.os", etc. + let is_client = name.contains("client"); + kiprintln!("Node {} is{} a client", name, if is_client {""} else {" not"}); + is_client + }) + .map(|name| { + let address: Address = (name, "llm-template", "llm-template", "template.os").into(); + address + }) + .collect(); + + kiprintln!("Found {} client addresses: {:?}", client_addresses.len(), client_addresses); + + if client_addresses.is_empty() { + return Err(anyhow::anyhow!("No client addresses found")); + } + + Ok(client_addresses) +} + + + diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/metadata.json b/src/new/templates/rust/ui/llm-template/test/llm-template-test/metadata.json new file mode 100644 index 00000000..c1ee9020 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/metadata.json @@ -0,0 +1,21 @@ +{ + "name": "llm-template Test", + "description": "A test for llm-template.", + "image": "", + "properties": { + "package_name": "llm-template-test", + "current_version": "0.1.0", + "publisher": "template.os", + "mirrors": [], + "code_hashes": { + "0.1.0": "" + }, + "wit_version": 1, + "dependencies": [ + "llm-template:template.os", + "tester:sys" + ] + }, + "external_url": "", + "animation_url": "" +} diff --git a/src/new/templates/rust/ui/llm-template/test/llm-template-test/pkg/manifest.json b/src/new/templates/rust/ui/llm-template/test/llm-template-test/pkg/manifest.json new file mode 100644 index 00000000..4d3be9d1 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/llm-template-test/pkg/manifest.json @@ -0,0 +1,17 @@ +[ + { + "process_name": "llm-template-test", + "process_wasm_path": "/llm-template-test.wasm", + "on_exit": "Restart", + "request_networking": false, + "request_capabilities": [ + "llm-template:llm-template:template.os", + "vfs:distro:sys" + ], + "grant_capabilities": [ + "llm-template:llm-template:template.os", + "vfs:distro:sys" + ], + "public": true + } +] diff --git a/src/new/templates/rust/ui/llm-template/test/tests.toml b/src/new/templates/rust/ui/llm-template/test/tests.toml new file mode 100644 index 00000000..42da8ee9 --- /dev/null +++ b/src/new/templates/rust/ui/llm-template/test/tests.toml @@ -0,0 +1,35 @@ +runtime = { FetchVersion = "latest" } +# runtime = { RepoPath = "~/git/hyperdrive" } +persist_home = true +runtime_build_release = false +always_print_node_output = false + + +[[tests]] +dependency_package_paths = [".."] +setup_packages = [ + { path = "..", run = true } +] +setup_scripts = [] +test_package_paths = ["llm-template-test"] +test_scripts = [] +timeout_secs = 100 +fakechain_router = 8545 + +[[tests.nodes]] +port = 8080 +home = "llm-template-test/results/client0" +fake_node_name = "client0.os" +runtime_verbosity = 2 + +[[tests.nodes]] +port = 8081 +home = "llm-template-test/results/client1" +fake_node_name = "client1.os" +runtime_verbosity = 2 + +[[tests.nodes]] +port = 8082 +home = "llm-template-test/results/client1" +fake_node_name = "client2.os" +runtime_verbosity = 2 diff --git a/src/new/templates/ui/llm-template/ui/.eslintrc.cjs b/src/new/templates/ui/llm-template/ui/.eslintrc.cjs new file mode 100644 index 00000000..d6c95379 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/src/new/templates/ui/llm-template/ui/.gitignore b/src/new/templates/ui/llm-template/ui/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/src/new/templates/ui/llm-template/ui/README.md b/src/new/templates/ui/llm-template/ui/README.md new file mode 100644 index 00000000..b0e896f4 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/README.md @@ -0,0 +1,56 @@ +# Hyperware UI Template + +Based on the Vite React Typescript template. + +## Setup + +When using `kit new`, the `BASE_URL` on line 9 of `vite.config.ts` will be set automatically. +The `BASE_URL` will be the first process in `manifest.json`, the `package` from `metadata.json`, and `publisher` from `metadata.json`. +If you have multiple processes in `manifest.json`, make sure the first process will be the one serving the UI. + +## Development + +Run `npm i` and then `npm run dev` to start working on the UI. + +You may see an error: + +``` +[vite] Pre-transform error: Failed to load url /our.js (resolved id: /our.js). Does the file exist? +``` + +You can safely ignore this error. The file will be served by the node via the proxy. + +## public vs assets + +The `public/assets` folder contains files that are referenced in `index.html`, `src/assets` is for asset files that are only referenced in `src` code. + +## About Vite + React + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default { + // other rules... + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: ['./tsconfig.json', './tsconfig.node.json'], + tsconfigRootDir: __dirname, + }, +} +``` + +- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` +- Optionally add `plugin:@typescript-eslint/stylistic-type-checked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list diff --git a/src/new/templates/ui/llm-template/ui/index.html b/src/new/templates/ui/llm-template/ui/index.html new file mode 100644 index 00000000..841f24c1 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + Simple LlmTemplate + + +
+ + + diff --git a/src/new/templates/ui/llm-template/ui/package-lock.json b/src/new/templates/ui/llm-template/ui/package-lock.json new file mode 100644 index 00000000..8f26017c --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/package-lock.json @@ -0,0 +1,3384 @@ +{ + "name": "ui-template", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ui-template", + "version": "0.0.0", + "dependencies": { + "@hyperware-ai/client-api": "^0.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "zustand": "^4.4.7" + }, + "devDependencies": { + "@types/node": "^20.10.4", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "http-proxy-middleware": "^2.0.6", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.6.tgz", + "integrity": "sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.6", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.6.tgz", + "integrity": "sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.6", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz", + "integrity": "sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.10.tgz", + "integrity": "sha512-7W0bK7qfkw1fc2viBfrtAEkDKHatYfHzr/jKAHNr9BvkYDXPcC6bodtm8AyLJNNuqClLNaeTLuwURt4PRT9d7w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.10.tgz", + "integrity": "sha512-1X4CClKhDgC3by7k8aOWZeBXQX8dHT5QAMCAQDArCLaYfkppoARvh0fit3X2Qs+MXDngKcHv6XXyQCpY0hkK1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.10.tgz", + "integrity": "sha512-O/nO/g+/7NlitUxETkUv/IvADKuZXyH4BHf/g/7laqKC4i/7whLpB0gvpPc2zpF0q9Q6FXS3TS75QHac9MvVWw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz", + "integrity": "sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.10.tgz", + "integrity": "sha512-alfGtT+IEICKtNE54hbvPg13xGBe4GkVxyGWtzr+yHO7HIiRJppPDhOKq3zstTcVf8msXb/t4eavW3jCDpMSmA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.10.tgz", + "integrity": "sha512-dMtk1wc7FSH8CCkE854GyGuNKCewlh+7heYP/sclpOG6Cectzk14qdUIY5CrKDbkA/OczXq9WesqnPl09mj5dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.10.tgz", + "integrity": "sha512-G5UPPspryHu1T3uX8WiOEUa6q6OlQh6gNl4CO4Iw5PS+Kg5bVggVFehzXBJY6X6RSOMS8iXDv2330VzaObm4Ag==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.10.tgz", + "integrity": "sha512-j6gUW5aAaPgD416Hk9FHxn27On28H4eVI9rJ4az7oCGTFW48+LcgNDBN+9f8rKZz7EEowo889CPKyeaD0iw9Kg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.10.tgz", + "integrity": "sha512-QxaouHWZ+2KWEj7cGJmvTIHVALfhpGxo3WLmlYfJ+dA5fJB6lDEIg+oe/0//FuyVHuS3l79/wyBxbHr0NgtxJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.10.tgz", + "integrity": "sha512-4ub1YwXxYjj9h1UIZs2hYbnTZBtenPw5NfXCRgEkGb0b6OJ2gpkMvDqRDYIDRjRdWSe/TBiZltm3Y3Q8SN1xNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.10.tgz", + "integrity": "sha512-lo3I9k+mbEKoxtoIbM0yC/MZ1i2wM0cIeOejlVdZ3D86LAcFXFRdeuZmh91QJvUTW51bOK5W2BznGNIl4+mDaA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.10.tgz", + "integrity": "sha512-J4gH3zhHNbdZN0Bcr1QUGVNkHTdpijgx5VMxeetSk6ntdt+vR1DqGmHxQYHRmNb77tP6GVvD+K0NyO4xjd7y4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.10.tgz", + "integrity": "sha512-tgT/7u+QhV6ge8wFMzaklOY7KqiyitgT1AUHMApau32ZlvTB/+efeCtMk4eXS+uEymYK249JsoiklZN64xt6oQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.10.tgz", + "integrity": "sha512-0f/spw0PfBMZBNqtKe5FLzBDGo0SKZKvMl5PHYQr3+eiSscfJ96XEknCe+JoOayybWUFQbcJTrk946i3j9uYZA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.10.tgz", + "integrity": "sha512-pZFe0OeskMHzHa9U38g+z8Yx5FNCLFtUnJtQMpwhS+r4S566aK2ci3t4NCP4tjt6d5j5uo4h7tExZMjeKoehAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.10.tgz", + "integrity": "sha512-SpYNEqg/6pZYoc+1zLCjVOYvxfZVZj6w0KROZ3Fje/QrM3nfvT2llI+wmKSrWuX6wmZeTapbarvuNNK/qepSgA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.10.tgz", + "integrity": "sha512-ACbZ0vXy9zksNArWlk2c38NdKg25+L9pr/mVaj9SUq6lHZu/35nx2xnQVRGLrC1KKQqJKRIB0q8GspiHI3J80Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.10.tgz", + "integrity": "sha512-PxcgvjdSjtgPMiPQrM3pwSaG4kGphP+bLSb+cihuP0LYdZv1epbAIecHVl5sD3npkfYBZ0ZnOjR878I7MdJDFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.10.tgz", + "integrity": "sha512-ZkIOtrRL8SEJjr+VHjmW0znkPs+oJXhlJbNwfI37rvgeMtk3sxOQevXPXjmAPZPigVTncvFqLMd+uV0IBSEzqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.10.tgz", + "integrity": "sha512-+Sa4oTDbpBfGpl3Hn3XiUe4f8TU2JF7aX8cOfqFYMMjXp6ma6NJDztl5FDG8Ezx0OjwGikIHw+iA54YLDNNVfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.10.tgz", + "integrity": "sha512-EOGVLK1oWMBXgfttJdPHDTiivYSjX6jDNaATeNOaCOFEVcfMjtbx7WVQwPSE1eIfCp/CaSF2nSrDtzc4I9f8TQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.10.tgz", + "integrity": "sha512-whqLG6Sc70AbU73fFYvuYzaE4MNMBIlR1Y/IrUeOXFrWHxBEjjbZaQ3IXIQS8wJdAzue2GwYZCjOrgrU1oUHoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@hyperware-ai/client-api": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@hyperware-ai/client-api/-/client-api-0.1.0.tgz", + "integrity": "sha512-qDj7xew+Z1qSQtiq8oc2ntZuiVdM1rtwIAQnyLxpL0j+f3cCfUnaDE6/604wJIrYmh8xjrtxUpWqu+ZmZoy6yg==", + "dependencies": { + "buffer": "^6.0.3", + "node-forge": "^1.3.1", + "typescript": "^4.9.5" + } + }, + "node_modules/@hyperware-ai/client-api/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.1.tgz", + "integrity": "sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.1.tgz", + "integrity": "sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz", + "integrity": "sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.1.tgz", + "integrity": "sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.1.tgz", + "integrity": "sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.1.tgz", + "integrity": "sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.1.tgz", + "integrity": "sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.1.tgz", + "integrity": "sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.1.tgz", + "integrity": "sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.1.tgz", + "integrity": "sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.1.tgz", + "integrity": "sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.1.tgz", + "integrity": "sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.1.tgz", + "integrity": "sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.10.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", + "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "devOptional": true + }, + "node_modules/@types/react": { + "version": "18.2.45", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz", + "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "devOptional": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.15.0.tgz", + "integrity": "sha512-j5qoikQqPccq9QoBAupOP+CBu8BaJ8BLjaXSioDISeTZkVO3ig7oSIKh3H+rEpee7xCXtWwSB4KIL5l6hWZzpg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/type-utils": "6.15.0", + "@typescript-eslint/utils": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.15.0.tgz", + "integrity": "sha512-MkgKNnsjC6QwcMdlNAel24jjkEO/0hQaMDLqP4S9zq5HBAUJNQB6y+3DwLjX7b3l2b37eNAxMPLwb3/kh8VKdA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz", + "integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.15.0.tgz", + "integrity": "sha512-CnmHKTfX6450Bo49hPg2OkIm/D/TVYV7jO1MCfPYGwf6x3GO0VU8YMO5AYMn+u3X05lRRxA4fWCz87GFQV6yVQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.15.0", + "@typescript-eslint/utils": "6.15.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz", + "integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz", + "integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/visitor-keys": "6.15.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz", + "integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.15.0", + "@typescript-eslint/types": "6.15.0", + "@typescript-eslint/typescript-estree": "6.15.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz", + "integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.15.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001571", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz", + "integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.616", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz", + "integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.10.tgz", + "integrity": "sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.10", + "@esbuild/android-arm": "0.19.10", + "@esbuild/android-arm64": "0.19.10", + "@esbuild/android-x64": "0.19.10", + "@esbuild/darwin-arm64": "0.19.10", + "@esbuild/darwin-x64": "0.19.10", + "@esbuild/freebsd-arm64": "0.19.10", + "@esbuild/freebsd-x64": "0.19.10", + "@esbuild/linux-arm": "0.19.10", + "@esbuild/linux-arm64": "0.19.10", + "@esbuild/linux-ia32": "0.19.10", + "@esbuild/linux-loong64": "0.19.10", + "@esbuild/linux-mips64el": "0.19.10", + "@esbuild/linux-ppc64": "0.19.10", + "@esbuild/linux-riscv64": "0.19.10", + "@esbuild/linux-s390x": "0.19.10", + "@esbuild/linux-x64": "0.19.10", + "@esbuild/netbsd-x64": "0.19.10", + "@esbuild/openbsd-x64": "0.19.10", + "@esbuild/sunos-x64": "0.19.10", + "@esbuild/win32-arm64": "0.19.10", + "@esbuild/win32-ia32": "0.19.10", + "@esbuild/win32-x64": "0.19.10" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz", + "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.1.tgz", + "integrity": "sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.1", + "@rollup/rollup-android-arm64": "4.9.1", + "@rollup/rollup-darwin-arm64": "4.9.1", + "@rollup/rollup-darwin-x64": "4.9.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.1", + "@rollup/rollup-linux-arm64-gnu": "4.9.1", + "@rollup/rollup-linux-arm64-musl": "4.9.1", + "@rollup/rollup-linux-riscv64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-musl": "4.9.1", + "@rollup/rollup-win32-arm64-msvc": "4.9.1", + "@rollup/rollup-win32-ia32-msvc": "4.9.1", + "@rollup/rollup-win32-x64-msvc": "4.9.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/vite": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.4.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.7.tgz", + "integrity": "sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + } +} diff --git a/src/new/templates/ui/llm-template/ui/package.json b/src/new/templates/ui/llm-template/ui/package.json new file mode 100644 index 00000000..ec9ddd50 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/package.json @@ -0,0 +1,35 @@ +{ + "name": "ui-template", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --port 3000", + "start": "vite --port 3000", + "build": "tsc && vite build", + "copy": "mkdir -p ../pkg/ui && rm -rf ../pkg/ui/* && cp -r dist/* ../pkg/ui/", + "build:copy": "npm run build && npm run copy", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@hyperware-ai/client-api": "^0.1.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "zustand": "^4.4.7" + }, + "devDependencies": { + "@types/node": "^20.10.4", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "http-proxy-middleware": "^2.0.6", + "typescript": "^5.2.2", + "vite": "^5.0.8" + } +} diff --git a/src/new/templates/ui/llm-template/ui/public/assets/vite.svg b/src/new/templates/ui/llm-template/ui/public/assets/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/public/assets/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/new/templates/ui/llm-template/ui/src/App.css b/src/new/templates/ui/llm-template/ui/src/App.css new file mode 100644 index 00000000..ce642b2a --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/App.css @@ -0,0 +1,296 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; + width: 75%; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 1em; + background: #242424; + padding: 1.5em; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.read-the-docs { + color: #888; +} + +.input-row { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + width: 100%; +} + +.input-row > button { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + padding: 0.3em 0.6em; +} + +.input-row > input { + border: 1px solid #ccc; + padding: 0.32em 0.6em; + font-size: 1em; + border-radius: 0.25em; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + width: 100%; +} + +.llm-template-button { + padding: 0.25em 0.5em; + margin-bottom: 1em; + background-color: cornflowerblue; +} + +.llm-template-button.selected { + background-color: blue; + color: white; + cursor: default; +} + +.message-list { + display: flex; + flex-direction: column; + margin-bottom: 0.5em; +} + +.message { + align-self: flex-start; + background-color: gray; + border-radius: 0.25em; + padding: 0.25em 0.5em; + margin-bottom: 0.5em; + color: white; +} + +.message.ours { + align-self: flex-end; + background-color: blue; + color: white; +} + +.tabs { + display: flex; + margin-bottom: 10px; + gap: 8px; +} + +.tabs button { + padding: 10px 20px; + border: 1px solid #2a2a2a; + background: #1a1a1a; + color: #ffffff; + cursor: pointer; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; + font-size: 0.9rem; + font-weight: 500; + border-radius: 4px; + transition: all 0.2s ease; +} + +.tabs button:hover { + background: #2a2a2a; +} + +.tabs button.active { + background: #3b82f6; + color: white; + border-color: #2563eb; +} + +.header-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding: 10px 0; +} + +.header-row h3 { + color: #ffffff; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; + font-size: 1.2rem; + margin: 0; +} + +.header-row button { + background: #3b82f6; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.header-row button:hover { + background: #2563eb; +} + +.message-table { + width: 100%; + border-collapse: collapse; + background: #1a1a1a; + border-radius: 8px; + overflow: hidden; +} + +.message-table th, .message-table td { + border: 1px solid #2a2a2a; + padding: 12px; + text-align: left; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; +} + +.message-table th { + background-color: #2a2a2a; + color: #ffffff; + font-weight: 600; + position: sticky; + top: 0; + z-index: 1; +} + +.message-table td { + color: #e5e5e5; +} + +.message-table tr:nth-child(even) { + background-color: #1f1f1f; +} + +.message-table tr:hover { + background-color: #2c2c2c; +} + +.ws-message-list { + list-style: none; + padding: 0; + margin: 0; + background: #1a1a1a; + border-radius: 8px; +} + +.ws-message-list li { + padding: 10px 15px; + border-bottom: 1px solid #2a2a2a; + color: #e5e5e5; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 0.9rem; +} + +.ws-message-list li:last-child { + border-bottom: none; +} + +.input-row { + display: flex; +} + +.input-row input { + flex: 1; + padding: 8px; + margin-right: 8px; +} + +pre { + background: #1a1a1a; + padding: 15px; + border-radius: 8px; + color: #e5e5e5; + font-family: 'Consolas', 'Monaco', monospace; + font-size: 0.9rem; + overflow-x: auto; +} + +select { + padding: 8px; + border-radius: 4px; + background: #1a1a1a; + color: #e5e5e5; + border: 1px solid #2a2a2a; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; + cursor: pointer; +} + +select:focus { + outline: none; + border-color: #3b82f6; +} + +label { + color: #e5e5e5; + font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; + margin-right: 8px; +} + +.ws-status { + display: flex; + align-items: center; + gap: 10px; + margin-top: 5px; + font-size: 0.9rem; + color: #e5e5e5; +} + +.status-connected { + color: #4ade80; +} + +.status-connecting { + color: #fbbf24; +} + +.status-disconnected { + color: #ef4444; +} + +.reconnect-button { + padding: 4px 8px; + font-size: 0.8rem; + background: #3b82f6; + border: none; + border-radius: 4px; + color: white; + cursor: pointer; +} + +.reconnect-button:hover { + background: #2563eb; +} diff --git a/src/new/templates/ui/llm-template/ui/src/App.tsx b/src/new/templates/ui/llm-template/ui/src/App.tsx new file mode 100644 index 00000000..d04819be --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/App.tsx @@ -0,0 +1,363 @@ +import { useState, useEffect } from "react"; +import reactLogo from "./assets/react.svg"; +import viteLogo from "./assets/vite.svg"; +import HyperwareClientApi from "@hyperware-ai/client-api"; +import "./App.css"; +import { + MessageLog, + StatusResponse +} from "./types/types"; +import { + fetchStatus, + fetchHistory, + clearHistory, + sendCustomMessage +} from "./utilities/api"; + +const BASE_URL = import.meta.env.BASE_URL; +if (window.our) window.our.process = BASE_URL?.replace("/", ""); + +const PROXY_TARGET = `${(import.meta.env.VITE_NODE_URL || "http://localhost:8080")}${BASE_URL}`; + +// This env also has BASE_URL which should match the process + package name +const WEBSOCKET_URL = import.meta.env.DEV + ? `${PROXY_TARGET.replace('http', 'ws')}` + : `${window.location.origin.replace('http', 'ws')}${BASE_URL}`; + +console.log('WEBSOCKET URL configured as:', WEBSOCKET_URL); + +function App() { + const [customMessage, setCustomMessage] = useState(""); + const [customType, setCustomType] = useState("info"); + const [statusData, setStatusData] = useState(null); + const [historyData, setHistoryData] = useState([]); + const [nodeConnected, setNodeConnected] = useState(true); + const [api, setApi] = useState(); + const [wsMessages, setWsMessages] = useState([]); + const [activeTab, setActiveTab] = useState("status"); + const [messageMethod, setMessageMethod] = useState<"http" | "websocket">("http"); + const [wsStatus, setWsStatus] = useState<'disconnected' | 'connecting' | 'connected'>('disconnected'); + + useEffect(() => { + let mounted = true; + + const connectWebSocket = async () => { + if (!WEBSOCKET_URL || wsStatus === 'connecting') return; + + try { + setWsStatus('connecting'); + console.log('Attempting to connect to WebSocket at:', WEBSOCKET_URL); + + const newApi = new HyperwareClientApi({ + uri: WEBSOCKET_URL, + nodeId: window.our?.node || 'unknown', + processId: window.our?.process || BASE_URL?.replace("/", "") || 'unknown', + onOpen: (_event, _api) => { + if (mounted) { + console.log("WebSocket Connected"); + setWsStatus('connected'); + setNodeConnected(true); + } + }, + onClose: () => { + if (mounted) { + console.log("WebSocket Disconnected"); + setWsStatus('disconnected'); + setApi(undefined); + } + }, + onMessage: (json, _api) => { + if (!mounted) return; + + console.log('WEBSOCKET MESSAGE', json); + try { + const data = JSON.parse(json); + console.log("WebSocket received message", data); + + setWsMessages(prev => [...prev, JSON.stringify(data)]); + + if (data.type === "status_update") { + setStatusData(data); + } + } catch (error) { + console.error("Error parsing WebSocket message", error); + } + }, + }); + + if (mounted) { + setApi(newApi); + } + } catch (error) { + console.error("Error connecting to WebSocket:", error); + if (mounted) { + setWsStatus('disconnected'); + setNodeConnected(false); + } + } + }; + + connectWebSocket(); + + // Cleanup function + return () => { + mounted = false; + setApi(undefined); + setWsStatus('disconnected'); + }; + }, []); // Empty dependency array to run only once + + const reconnectWebSocket = () => { + setApi(undefined); + setWsStatus('disconnected'); + // The WebSocket will automatically reconnect due to the useEffect dependency + }; + + const handleFetchStatus = async () => { + const status = await fetchStatus(); + if (status) { + setStatusData(status); + } else { + setNodeConnected(false); + } + }; + + const handleFetchHistory = async () => { + const history = await fetchHistory(); + setHistoryData(history); + }; + + const handleClearHistory = async () => { + const success = await clearHistory(); + if (success) { + setHistoryData([]); + setWsMessages(prev => [...prev, "History cleared"]); + } + }; + + const handleSendCustomMessage = async (event: React.FormEvent) => { + event.preventDefault(); + + const success = await sendCustomMessage(customMessage, customType, messageMethod, api); + if (success) { + setCustomMessage(""); + setWsMessages(prev => [...prev, `Sent: ${JSON.stringify({ + CustomMessage: { + message_type: customType, + content: customMessage + } + })}`]); + + // Refresh data after sending a message + if (activeTab === "history") { + handleFetchHistory(); + } else { + handleFetchStatus(); + } + } + }; + + // When tab changes, fetch appropriate data + useEffect(() => { + if (activeTab === "history") { + handleFetchHistory(); + } else if (activeTab === "status") { + handleFetchStatus(); + } + }, [activeTab]); + + return ( +
+
+ Node ID: {window.our?.node} +
+ {!nodeConnected && ( +
+

Node not connected

+

+ You need to start a node at {PROXY_TARGET} before you can use this UI + in development. +

+
+ )} +

Hyperware LLM Template

+
+
+ + + +
+ +
+
+

Send Custom Message

+
+
+
+ + +
+
+ + +
+
+
+ setCustomMessage(event.target.value)} + autoFocus + /> + +
+
+ + {activeTab === "status" && ( +
+
+

System Status

+ +
+
+                  {statusData ? JSON.stringify(statusData, null, 2) : "No data yet"}
+                
+
+ )} + + {activeTab === "history" && ( +
+
+

Message History

+
+ + +
+
+
+ {historyData.length > 0 ? ( + + + + + + + + + + + + {historyData.map((msg, index) => ( + + + + + + + + ))} + +
TimeSourceChannelTypeContent
{new Date(msg.timestamp * 1000).toLocaleTimeString()}{msg.source}{msg.channel}{typeof msg.message_type === 'string' ? msg.message_type : JSON.stringify(msg.message_type)}{msg.content || "-"}
+ ) : ( +

No message history available

+ )} +
+
+ )} + + {activeTab === "websocket" && ( +
+
+
+

WebSocket Messages

+
+ Status: {wsStatus} + {wsStatus !== 'connected' && ( + + )} +
+
+ +
+
+
    + {wsMessages.length > 0 ? ( + wsMessages.map((msg, index) => ( +
  • {msg}
  • + )) + ) : ( +

    No WebSocket messages yet

    + )} +
+
+
+ )} +
+
+
+
+ ); +} + +export default App; diff --git a/src/new/templates/ui/llm-template/ui/src/index.css b/src/new/templates/ui/llm-template/ui/src/index.css new file mode 100644 index 00000000..22463e3e --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/index.css @@ -0,0 +1,72 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +ul { + list-style: none; + padding: 0; + margin: 0; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + background-color: darkblue; + color: white; + font-size: 1em; + font-weight: 500; + font-family: inherit; + cursor: pointer; + transition: background-color 0.25s; +} +button:hover { + background-color: blue; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } +} diff --git a/src/new/templates/ui/llm-template/ui/src/main.tsx b/src/new/templates/ui/llm-template/ui/src/main.tsx new file mode 100644 index 00000000..3d7150da --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/new/templates/ui/llm-template/ui/src/store/llm_template.ts b/src/new/templates/ui/llm-template/ui/src/store/llm_template.ts new file mode 100644 index 00000000..4b717294 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/store/llm_template.ts @@ -0,0 +1 @@ +// Placeholder for store \ No newline at end of file diff --git a/src/new/templates/ui/llm-template/ui/src/types/global.ts b/src/new/templates/ui/llm-template/ui/src/types/global.ts new file mode 100644 index 00000000..44640a4c --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/types/global.ts @@ -0,0 +1,10 @@ +declare module "global" { + global { + interface Window { + our: { + node: string; + process: string; + }; + } + } +} diff --git a/src/new/templates/ui/llm-template/ui/src/types/types.ts b/src/new/templates/ui/llm-template/ui/src/types/types.ts new file mode 100644 index 00000000..9fd8911c --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/types/types.ts @@ -0,0 +1,14 @@ +// Define interfaces for response types +export interface MessageLog { + source: string; + channel: string; + message_type: string | { [key: string]: any }; + content?: string; + timestamp: number; + } + +export interface StatusResponse { + connected_clients: number; + message_count: number; + message_counts_by_channel: { [key: string]: number }; + } \ No newline at end of file diff --git a/src/new/templates/ui/llm-template/ui/src/utilities/api.ts b/src/new/templates/ui/llm-template/ui/src/utilities/api.ts new file mode 100644 index 00000000..c5fd659c --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/utilities/api.ts @@ -0,0 +1,129 @@ +import { MessageLog, StatusResponse } from "../types/types"; + +const BASE_URL = import.meta.env.BASE_URL; + +export const fetchStatus = async (): Promise => { + try { + const response = await fetch(`${BASE_URL}/api/status`, { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to fetch status"); + } + + const data = await response.json(); + console.log("Status response:", data); + + if (data.Status) { + return data.Status; + } else { + console.error("Unexpected response format:", data); + throw new Error("Invalid response format"); + } + } catch (error) { + console.error("Error fetching status:", error); + return null; + } +}; + +export const fetchHistory = async (): Promise => { + try { + const response = await fetch(`${BASE_URL}/api/history`, { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to fetch history"); + } + + const data = await response.json(); + console.log("History response:", data); + + if (data.History && Array.isArray(data.History.messages)) { + return data.History.messages; + } else { + console.error("Unexpected response format:", data); + throw new Error("Invalid response format"); + } + } catch (error) { + console.error("Error fetching history:", error); + return []; + } +}; + +export const clearHistory = async (): Promise => { + try { + const requestData = { + ClearHistory: null + }; + + const response = await fetch(`${BASE_URL}/api`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(requestData) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to clear history"); + } + + return true; + } catch (error) { + console.error("Error clearing history:", error); + return false; + } +}; + +export const sendCustomMessage = async ( + message: string, + messageType: string, + messageMethod: "http" | "websocket", + api?: any +): Promise => { + if (!message) return false; + + const requestData = { + CustomMessage: { + message_type: messageType, + content: message + } + }; + + try { + if (messageMethod === "websocket") { + if (!api) { + throw new Error("WebSocket not connected"); + } + api.send({ data: requestData }); + } else { + const response = await fetch(`${BASE_URL}/api`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(requestData) + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to send custom message"); + } + } + return true; + } catch (error) { + console.error("Error sending message:", error); + return false; + } +}; \ No newline at end of file diff --git a/src/new/templates/ui/llm-template/ui/src/vite-env.d.ts b/src/new/templates/ui/llm-template/ui/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/src/new/templates/ui/llm-template/ui/tsconfig.json b/src/new/templates/ui/llm-template/ui/tsconfig.json new file mode 100644 index 00000000..6f148432 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + // "strict": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "src", + "../pkg/manifest.json", + "../pkg/metadata.json", + ], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/src/new/templates/ui/llm-template/ui/tsconfig.node.json b/src/new/templates/ui/llm-template/ui/tsconfig.node.json new file mode 100644 index 00000000..42872c59 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/src/new/templates/ui/llm-template/ui/vite.config.ts b/src/new/templates/ui/llm-template/ui/vite.config.ts new file mode 100644 index 00000000..3683e564 --- /dev/null +++ b/src/new/templates/ui/llm-template/ui/vite.config.ts @@ -0,0 +1,67 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +/* +If you are developing a UI outside of a Hyperware project, +comment out the following 2 lines: +*/ +import manifest from '../pkg/manifest.json' +import metadata from '../metadata.json' + +/* +IMPORTANT: +This must match the process name from pkg/manifest.json + pkg/metadata.json +The format is "/" + "process_name:package_name:publisher_node" +*/ +const BASE_URL = `/${manifest[0].process_name}:${metadata.properties.package_name}:${metadata.properties.publisher}`; + +// This is the proxy URL, it must match the node you are developing against +const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080').replace('localhost', '127.0.0.1'); + +console.log('process.env.VITE_NODE_URL', process.env.VITE_NODE_URL, PROXY_URL); + +export default defineConfig({ + plugins: [react()], + base: BASE_URL, + build: { + rollupOptions: { + external: ['/our.js'] + } + }, + server: { + open: true, + proxy: { + '/our': { + target: PROXY_URL, + changeOrigin: true, + }, + [`${BASE_URL}/our.js`]: { + target: PROXY_URL, + changeOrigin: true, + rewrite: (path) => path.replace(BASE_URL, ''), + }, + // This route will match all other HTTP requests to the backend + [`^${BASE_URL}/(?!(@vite/client|src/.*|node_modules/.*|@react-refresh|$))`]: { + target: PROXY_URL, + changeOrigin: true, + }, + // '/example': { + // target: PROXY_URL, + // changeOrigin: true, + // rewrite: (path) => path.replace(BASE_URL, ''), + // // This is only for debugging purposes + // configure: (proxy, _options) => { + // proxy.on('error', (err, _req, _res) => { + // console.log('proxy error', err); + // }); + // proxy.on('proxyReq', (proxyReq, req, _res) => { + // console.log('Sending Request to the Target:', req.method, req.url); + // }); + // proxy.on('proxyRes', (proxyRes, req, _res) => { + // console.log('Received Response from the Target:', proxyRes.statusCode, req.url); + // }); + // }, + // }, + } + } +});