From 2568bfde5e94bac25e3e8353daeb6943bdc4bca9 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 31 May 2024 13:46:58 -0600 Subject: [PATCH 01/20] create skeleton --- Makefile | 2 +- core/build-analyticsd.sh | 42 ++++++ core/{build-reg.sh => build-registrybox.sh} | 0 core/{build-prod.sh => build-startos-bins.sh} | 0 core/startos/Cargo.toml | 7 +- core/startos/src/analytics/context.rs | 121 ++++++++++++++++++ core/startos/src/analytics/mod.rs | 78 +++++++++++ core/startos/src/bins/analyticsd.rs | 86 +++++++++++++ core/startos/src/bins/mod.rs | 4 + core/startos/src/context/config.rs | 3 + core/startos/src/context/rpc.rs | 5 + core/startos/src/lib.rs | 9 +- core/startos/src/lxc/dev.rs | 112 ++++++++++++++++ core/startos/src/lxc/mod.rs | 117 +---------------- core/startos/src/os_install/mod.rs | 4 + core/startos/src/registry/context.rs | 21 ++- sdk/lib/osBindings/AddAssetParams.ts | 1 - sdk/lib/osBindings/AddPackageParams.ts | 9 ++ sdk/lib/osBindings/GetPackageResponse.ts | 1 + sdk/lib/osBindings/GetPackageResponseFull.ts | 1 + sdk/lib/osBindings/HardwareRequirements.ts | 2 +- sdk/lib/osBindings/OsVersionInfo.ts | 2 +- sdk/lib/osBindings/PackageInfo.ts | 3 +- sdk/lib/osBindings/PackageVersionInfo.ts | 1 - sdk/lib/osBindings/index.ts | 1 + 25 files changed, 508 insertions(+), 124 deletions(-) create mode 100755 core/build-analyticsd.sh rename core/{build-reg.sh => build-registrybox.sh} (100%) rename core/{build-prod.sh => build-startos-bins.sh} (100%) create mode 100644 core/startos/src/analytics/context.rs create mode 100644 core/startos/src/analytics/mod.rs create mode 100644 core/startos/src/bins/analyticsd.rs create mode 100644 core/startos/src/lxc/dev.rs create mode 100644 sdk/lib/osBindings/AddPackageParams.ts diff --git a/Makefile b/Makefile index ff5b9c4ad..7b488d2d2 100644 --- a/Makefile +++ b/Makefile @@ -225,7 +225,7 @@ system-images/binfmt/docker-images/$(ARCH).tar: $(BINFMT_SRC) cd system-images/binfmt && make docker-images/$(ARCH).tar && touch docker-images/$(ARCH).tar $(BINS): $(CORE_SRC) $(ENVIRONMENT_FILE) - cd core && ARCH=$(ARCH) ./build-prod.sh + cd core && ARCH=$(ARCH) ./build-startos-bins.sh touch $(BINS) web/node_modules/.package-lock.json: web/package.json sdk/dist diff --git a/core/build-analyticsd.sh b/core/build-analyticsd.sh new file mode 100755 index 000000000..4f8578da1 --- /dev/null +++ b/core/build-analyticsd.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +cd "$(dirname "${BASH_SOURCE[0]}")" + +set -e +shopt -s expand_aliases + +if [ -z "$ARCH" ]; then + ARCH=$(uname -m) +fi + +USE_TTY= +if tty -s; then + USE_TTY="-it" +fi + +cd .. +FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')" +RUSTFLAGS="" + +if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then + RUSTFLAGS="--cfg tokio_unstable" +fi + +alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl' + +set +e +fail= +echo "FEATURES=\"$FEATURES\"" +echo "RUSTFLAGS=\"$RUSTFLAGS\"" +if ! rust-musl-builder sh -c "(cd core && cargo build --release --no-default-features --features analyticsd,$FEATURES --locked --bin analyticsd --target=$ARCH-unknown-linux-musl)"; then + fail=true +fi +set -e +cd core + +sudo chown -R $USER target +sudo chown -R $USER ~/.cargo + +if [ -n "$fail" ]; then + exit 1 +fi diff --git a/core/build-reg.sh b/core/build-registrybox.sh similarity index 100% rename from core/build-reg.sh rename to core/build-registrybox.sh diff --git a/core/build-prod.sh b/core/build-startos-bins.sh similarity index 100% rename from core/build-prod.sh rename to core/build-startos-bins.sh diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index bd064a167..fa2bac69d 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -37,12 +37,17 @@ path = "src/main.rs" name = "registrybox" path = "src/main.rs" +[[bin]] +name = "analyticsd" +path = "src/main.rs" + [features] cli = [] container-runtime = [] daemon = [] registry = [] -default = ["cli", "daemon", "registry"] +analyticsd = [] +default = ["cli", "daemon"] dev = [] unstable = ["console-subscriber", "tokio/tracing"] docker = [] diff --git a/core/startos/src/analytics/context.rs b/core/startos/src/analytics/context.rs new file mode 100644 index 000000000..8ece8479b --- /dev/null +++ b/core/startos/src/analytics/context.rs @@ -0,0 +1,121 @@ +use std::net::{Ipv4Addr, SocketAddr}; +use std::ops::Deref; +use std::path::PathBuf; +use std::sync::Arc; + +use clap::Parser; +use reqwest::{Client, Proxy}; +use rpc_toolkit::yajrc::RpcError; +use rpc_toolkit::{call_remote_http, CallRemote, Context, Empty}; +use serde::{Deserialize, Serialize}; +use sqlx::PgPool; +use tokio::sync::broadcast::Sender; +use tracing::instrument; +use url::Url; + +use crate::context::config::{ContextConfig, CONFIG_PATH}; +use crate::context::RpcContext; +use crate::prelude::*; +use crate::rpc_continuations::RpcContinuations; + +#[derive(Debug, Clone, Default, Deserialize, Serialize, Parser)] +#[serde(rename_all = "kebab-case")] +#[command(rename_all = "kebab-case")] +pub struct AnalyticsConfig { + #[arg(short = 'c', long = "config")] + pub config: Option, + #[arg(short = 'l', long = "listen")] + pub listen: Option, + #[arg(short = 'p', long = "proxy")] + pub tor_proxy: Option, + #[arg(short = 'd', long = "dbconnect")] + pub dbconnect: Option, +} +impl ContextConfig for AnalyticsConfig { + fn next(&mut self) -> Option { + self.config.take() + } + fn merge_with(&mut self, other: Self) { + self.listen = self.listen.take().or(other.listen); + self.tor_proxy = self.tor_proxy.take().or(other.tor_proxy); + self.dbconnect = self.dbconnect.take().or(other.dbconnect); + } +} + +impl AnalyticsConfig { + pub fn load(mut self) -> Result { + let path = self.next(); + self.load_path_rec(path)?; + self.load_path_rec(Some(CONFIG_PATH))?; + Ok(self) + } +} + +pub struct AnalyticsContextSeed { + pub listen: SocketAddr, + pub db: PgPool, + pub rpc_continuations: RpcContinuations, + pub client: Client, + pub shutdown: Sender<()>, +} + +#[derive(Clone)] +pub struct AnalyticsContext(Arc); +impl AnalyticsContext { + #[instrument(skip_all)] + pub async fn init(config: &AnalyticsConfig) -> Result { + let (shutdown, _) = tokio::sync::broadcast::channel(1); + let dbconnect = config + .dbconnect + .clone() + .unwrap_or_else(|| "postgres://localhost/analytics".parse().unwrap()) + .to_owned(); + let db = PgPool::connect(dbconnect.as_str()).await?; + let tor_proxy_url = config + .tor_proxy + .clone() + .map(Ok) + .unwrap_or_else(|| "socks5h://localhost:9050".parse())?; + Ok(Self(Arc::new(AnalyticsContextSeed { + listen: config + .listen + .unwrap_or(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 5959)), + db, + rpc_continuations: RpcContinuations::new(), + client: Client::builder() + .proxy(Proxy::custom(move |url| { + if url.host_str().map_or(false, |h| h.ends_with(".onion")) { + Some(tor_proxy_url.clone()) + } else { + None + } + })) + .build() + .with_kind(crate::ErrorKind::ParseUrl)?, + shutdown, + }))) + } +} +impl AsRef for AnalyticsContext { + fn as_ref(&self) -> &RpcContinuations { + &self.rpc_continuations + } +} + +impl Context for AnalyticsContext {} +impl Deref for AnalyticsContext { + type Target = AnalyticsContextSeed; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} + +impl CallRemote for RpcContext { + async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result { + if let Some(analytics_url) = self.analytics_url.clone() { + call_remote_http(&self.client, analytics_url, method, params).await + } else { + Ok(Value::Null) + } + } +} diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs new file mode 100644 index 000000000..52ca37948 --- /dev/null +++ b/core/startos/src/analytics/mod.rs @@ -0,0 +1,78 @@ +use std::net::SocketAddr; + +use axum::Router; +use futures::future::ready; +use rpc_toolkit::{Context, ParentHandler, Server}; + +use crate::analytics::context::AnalyticsContext; +use crate::middleware::cors::Cors; +use crate::net::static_server::{bad_request, not_found, server_error}; +use crate::net::web_server::WebServer; +use crate::rpc_continuations::Guid; + +pub mod context; + +pub fn analytics_api() -> ParentHandler { + ParentHandler::new() // TODO: FullMetal +} + +pub fn analytics_server_router(ctx: AnalyticsContext) -> Router { + use axum::extract as x; + use axum::routing::{any, get, post}; + Router::new() + .route("/rpc/*path", { + let ctx = ctx.clone(); + post( + Server::new(move || ready(Ok(ctx.clone())), analytics_api()) + .middleware(Cors::new()) + ) + }) + .route( + "/ws/rpc/*path", + get({ + let ctx = ctx.clone(); + move |x::Path(path): x::Path, + ws: axum::extract::ws::WebSocketUpgrade| async move { + match Guid::from(&path) { + None => { + tracing::debug!("No Guid Path"); + bad_request() + } + Some(guid) => match ctx.rpc_continuations.get_ws_handler(&guid).await { + Some(cont) => ws.on_upgrade(cont), + _ => not_found(), + }, + } + } + }), + ) + .route( + "/rest/rpc/*path", + any({ + let ctx = ctx.clone(); + move |request: x::Request| async move { + let path = request + .uri() + .path() + .strip_prefix("/rest/rpc/") + .unwrap_or_default(); + match Guid::from(&path) { + None => { + tracing::debug!("No Guid Path"); + bad_request() + } + Some(guid) => match ctx.rpc_continuations.get_rest_handler(&guid).await { + None => not_found(), + Some(cont) => cont(request).await.unwrap_or_else(server_error), + }, + } + } + }), + ) +} + +impl WebServer { + pub fn analytics(bind: SocketAddr, ctx: AnalyticsContext) -> Self { + Self::new(bind, analytics_server_router(ctx)) + } +} diff --git a/core/startos/src/bins/analyticsd.rs b/core/startos/src/bins/analyticsd.rs new file mode 100644 index 000000000..e37a7dae1 --- /dev/null +++ b/core/startos/src/bins/analyticsd.rs @@ -0,0 +1,86 @@ +use std::ffi::OsString; + +use clap::Parser; +use futures::FutureExt; +use tokio::signal::unix::signal; +use tracing::instrument; + +use crate::analytics::context::{AnalyticsConfig, AnalyticsContext}; +use crate::net::web_server::WebServer; +use crate::prelude::*; +use crate::util::logger::EmbassyLogger; + +#[instrument(skip_all)] +async fn inner_main(config: &AnalyticsConfig) -> Result<(), Error> { + let server = async { + let ctx = AnalyticsContext::init(config).await?; + let server = WebServer::analytics(ctx.listen, ctx.clone()); + + let mut shutdown_recv = ctx.shutdown.subscribe(); + + let sig_handler_ctx = ctx; + let sig_handler = tokio::spawn(async move { + use tokio::signal::unix::SignalKind; + futures::future::select_all( + [ + SignalKind::interrupt(), + SignalKind::quit(), + SignalKind::terminate(), + ] + .iter() + .map(|s| { + async move { + signal(*s) + .unwrap_or_else(|_| panic!("register {:?} handler", s)) + .recv() + .await + } + .boxed() + }), + ) + .await; + sig_handler_ctx + .shutdown + .send(()) + .map_err(|_| ()) + .expect("send shutdown signal"); + }); + + shutdown_recv + .recv() + .await + .with_kind(crate::ErrorKind::Unknown)?; + + sig_handler.abort(); + + Ok::<_, Error>(server) + } + .await?; + server.shutdown().await; + + Ok(()) +} + +pub fn main(args: impl IntoIterator) { + EmbassyLogger::init(); + + let config = AnalyticsConfig::parse_from(args).load().unwrap(); + + let res = { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to initialize runtime"); + rt.block_on(inner_main(&config)) + }; + + match res { + Ok(()) => (), + Err(e) => { + eprintln!("{}", e.source); + tracing::debug!("{:?}", e.source); + drop(e.source); + std::process::exit(e.kind as i32) + } + } +} diff --git a/core/startos/src/bins/mod.rs b/core/startos/src/bins/mod.rs index 4a4670a5b..70f87aa99 100644 --- a/core/startos/src/bins/mod.rs +++ b/core/startos/src/bins/mod.rs @@ -2,6 +2,8 @@ use std::collections::VecDeque; use std::ffi::OsString; use std::path::Path; +#[cfg(feature = "analytics")] +pub mod analytics; #[cfg(feature = "container-runtime")] pub mod container_cli; pub mod deprecated; @@ -24,6 +26,8 @@ fn select_executable(name: &str) -> Option)> { "startd" => Some(startd::main), #[cfg(feature = "registry")] "registry" => Some(registry::main), + #[cfg(feature = "analyticsd")] + "analyticsd" => Some(analyticsd::main), "embassy-cli" => Some(|_| deprecated::renamed("embassy-cli", "start-cli")), "embassy-sdk" => Some(|_| deprecated::renamed("embassy-sdk", "start-sdk")), "embassyd" => Some(|_| deprecated::renamed("embassyd", "startd")), diff --git a/core/startos/src/context/config.rs b/core/startos/src/context/config.rs index bc2da00e2..886361777 100644 --- a/core/startos/src/context/config.rs +++ b/core/startos/src/context/config.rs @@ -113,6 +113,8 @@ pub struct ServerConfig { pub datadir: Option, #[arg(long = "disable-encryption")] pub disable_encryption: Option, + #[arg(short = 'a', long = "analytics-url")] + pub analytics_url: Option, } impl ContextConfig for ServerConfig { fn next(&mut self) -> Option { @@ -131,6 +133,7 @@ impl ContextConfig for ServerConfig { .or(other.revision_cache_size); self.datadir = self.datadir.take().or(other.datadir); self.disable_encryption = self.disable_encryption.take().or(other.disable_encryption); + self.analytics_url = self.analytics_url.take().or(other.analytics_url); } } diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index cf2d28085..1042def74 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -13,6 +13,7 @@ use rpc_toolkit::{CallRemote, Context, Empty}; use tokio::sync::{broadcast, oneshot, Mutex, RwLock}; use tokio::time::Instant; use tracing::instrument; +use url::Url; use super::setup::CURRENT_SECRET; use crate::account::AccountInfo; @@ -55,7 +56,9 @@ pub struct RpcContextSeed { pub client: Client, pub hardware: Hardware, pub start_time: Instant, + #[cfg(feature = "dev")] pub dev: Dev, + pub analytics_url: Option, } pub struct Dev { @@ -184,9 +187,11 @@ impl RpcContext { .with_kind(crate::ErrorKind::ParseUrl)?, hardware: Hardware { devices, ram }, start_time: Instant::now(), + #[cfg(feature = "dev")] dev: Dev { lxc: Mutex::new(BTreeMap::new()), }, + analytics_url: config.analytics_url.clone(), }); let res = Self(seed.clone()); diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index d60a2db24..96210114f 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -24,6 +24,7 @@ lazy_static::lazy_static! { pub mod account; pub mod action; +pub mod analytics; pub mod auth; pub mod backup; pub mod bins; @@ -95,7 +96,7 @@ pub fn echo(_: C, EchoParams { message }: EchoParams) -> Result() -> ParentHandler { - ParentHandler::new() + let api = ParentHandler::new() .subcommand::("git-info", from_fn(version::git_info)) .subcommand( "echo", @@ -120,9 +121,11 @@ pub fn main_api() -> ParentHandler { ) .no_cli(), ) - .subcommand("lxc", lxc::lxc::()) .subcommand("s9pk", s9pk::rpc::s9pk()) - .subcommand("util", util::rpc::util::()) + .subcommand("util", util::rpc::util::()); + #[cfg(feature = "dev")] + let api = api.subcommand("lxc", lxc::dev::lxc::()); + api } pub fn server() -> ParentHandler { diff --git a/core/startos/src/lxc/dev.rs b/core/startos/src/lxc/dev.rs new file mode 100644 index 000000000..61dd8e598 --- /dev/null +++ b/core/startos/src/lxc/dev.rs @@ -0,0 +1,112 @@ +use std::ops::Deref; + +use clap::Parser; +use rpc_toolkit::{ + from_fn_async, CallRemoteHandler, Context, Empty, HandlerArgs, HandlerExt, HandlerFor, + ParentHandler, +}; +use serde::{Deserialize, Serialize}; +use ts_rs::TS; + +use crate::context::{CliContext, RpcContext}; +use crate::lxc::{ContainerId, LxcConfig}; +use crate::prelude::*; +use crate::rpc_continuations::Guid; + +pub fn lxc() -> ParentHandler { + ParentHandler::new() + .subcommand( + "create", + from_fn_async(create).with_call_remote::(), + ) + .subcommand( + "list", + from_fn_async(list) + .with_custom_display_fn(|_, res| { + use prettytable::*; + let mut table = table!([bc => "GUID"]); + for guid in res { + table.add_row(row![&*guid]); + } + table.printstd(); + Ok(()) + }) + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove) + .no_display() + .with_call_remote::(), + ) + .subcommand("connect", from_fn_async(connect_rpc).no_cli()) + .subcommand("connect", from_fn_async(connect_rpc_cli).no_display()) +} + +pub async fn create(ctx: RpcContext) -> Result { + let container = ctx.lxc_manager.create(None, LxcConfig::default()).await?; + let guid = container.guid.deref().clone(); + ctx.dev.lxc.lock().await.insert(guid.clone(), container); + Ok(guid) +} + +pub async fn list(ctx: RpcContext) -> Result, Error> { + Ok(ctx.dev.lxc.lock().await.keys().cloned().collect()) +} + +#[derive(Deserialize, Serialize, Parser, TS)] +pub struct RemoveParams { + #[ts(type = "string")] + pub guid: ContainerId, +} + +pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> { + if let Some(container) = ctx.dev.lxc.lock().await.remove(&guid) { + container.exit().await?; + } + Ok(()) +} + +#[derive(Deserialize, Serialize, Parser, TS)] +pub struct ConnectParams { + #[ts(type = "string")] + pub guid: ContainerId, +} + +pub async fn connect_rpc( + ctx: RpcContext, + ConnectParams { guid }: ConnectParams, +) -> Result { + super::connect( + &ctx, + ctx.dev.lxc.lock().await.get(&guid).ok_or_else(|| { + Error::new(eyre!("No container with guid: {guid}"), ErrorKind::NotFound) + })?, + ) + .await +} + +pub async fn connect_rpc_cli( + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgs, +) -> Result<(), Error> { + let ctx = context.clone(); + let guid = CallRemoteHandler::::new(from_fn_async(connect_rpc)) + .handle_async(HandlerArgs { + context, + parent_method, + method, + params: rpc_toolkit::util::Flat(params, Empty {}), + inherited_params, + raw_params, + }) + .await?; + + super::connect_cli(&ctx, guid).await +} diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index f64ecebe7..997c17c8a 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -1,20 +1,15 @@ use std::collections::BTreeSet; use std::net::Ipv4Addr; -use std::ops::Deref; use std::path::Path; use std::sync::{Arc, Weak}; use std::time::Duration; use clap::builder::ValueParserFactory; -use clap::Parser; use futures::{AsyncWriteExt, FutureExt, StreamExt}; use imbl_value::{InOMap, InternedString}; use models::InvalidId; -use rpc_toolkit::yajrc::{RpcError, RpcResponse}; -use rpc_toolkit::{ - from_fn_async, CallRemoteHandler, Context, Empty, GenericRpcMethod, HandlerArgs, HandlerExt, - HandlerFor, ParentHandler, RpcRequest, -}; +use rpc_toolkit::yajrc::RpcError; +use rpc_toolkit::{GenericRpcMethod, RpcRequest, RpcResponse}; use rustyline_async::{ReadlineEvent, SharedWriter}; use serde::{Deserialize, Serialize}; use tokio::fs::File; @@ -38,6 +33,9 @@ use crate::util::clap::FromStrParser; use crate::util::rpc_client::UnixRpcClient; use crate::util::{new_guid, Invoke}; +#[cfg(feature = "dev")] +pub mod dev; + const LXC_CONTAINER_DIR: &str = "/var/lib/lxc"; const RPC_DIR: &str = "media/startos/rpc"; // must not be absolute path pub const CONTAINER_RPC_SERVER_SOCKET: &str = "service.sock"; // must not be absolute path @@ -369,80 +367,6 @@ impl Drop for LxcContainer { #[derive(Default, Serialize)] pub struct LxcConfig {} - -pub fn lxc() -> ParentHandler { - ParentHandler::new() - .subcommand( - "create", - from_fn_async(create).with_call_remote::(), - ) - .subcommand( - "list", - from_fn_async(list) - .with_custom_display_fn(|_, res| { - use prettytable::*; - let mut table = table!([bc => "GUID"]); - for guid in res { - table.add_row(row![&*guid]); - } - table.printstd(); - Ok(()) - }) - .with_call_remote::(), - ) - .subcommand( - "remove", - from_fn_async(remove) - .no_display() - .with_call_remote::(), - ) - .subcommand("connect", from_fn_async(connect_rpc).no_cli()) - .subcommand("connect", from_fn_async(connect_rpc_cli).no_display()) -} - -pub async fn create(ctx: RpcContext) -> Result { - let container = ctx.lxc_manager.create(None, LxcConfig::default()).await?; - let guid = container.guid.deref().clone(); - ctx.dev.lxc.lock().await.insert(guid.clone(), container); - Ok(guid) -} - -pub async fn list(ctx: RpcContext) -> Result, Error> { - Ok(ctx.dev.lxc.lock().await.keys().cloned().collect()) -} - -#[derive(Deserialize, Serialize, Parser, TS)] -pub struct RemoveParams { - #[ts(type = "string")] - pub guid: ContainerId, -} - -pub async fn remove(ctx: RpcContext, RemoveParams { guid }: RemoveParams) -> Result<(), Error> { - if let Some(container) = ctx.dev.lxc.lock().await.remove(&guid) { - container.exit().await?; - } - Ok(()) -} - -#[derive(Deserialize, Serialize, Parser, TS)] -pub struct ConnectParams { - #[ts(type = "string")] - pub guid: ContainerId, -} - -pub async fn connect_rpc( - ctx: RpcContext, - ConnectParams { guid }: ConnectParams, -) -> Result { - connect( - &ctx, - ctx.dev.lxc.lock().await.get(&guid).ok_or_else(|| { - Error::new(eyre!("No container with guid: {guid}"), ErrorKind::NotFound) - })?, - ) - .await -} - pub async fn connect(ctx: &RpcContext, container: &LxcContainer) -> Result { use axum::extract::ws::Message; @@ -473,10 +397,8 @@ pub async fn connect(ctx: &RpcContext, container: &LxcContainer) -> Result { id, result }, - ) - .with_kind(ErrorKind::Serialization)?, + serde_json::to_string(&RpcResponse { id, result }) + .with_kind(ErrorKind::Serialization)?, )) .await .with_kind(ErrorKind::Network)?; @@ -612,28 +534,3 @@ pub async fn connect_cli(ctx: &CliContext, guid: Guid) -> Result<(), Error> { Ok(()) } - -pub async fn connect_rpc_cli( - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgs, -) -> Result<(), Error> { - let ctx = context.clone(); - let guid = CallRemoteHandler::::new(from_fn_async(connect_rpc)) - .handle_async(HandlerArgs { - context, - parent_method, - method, - params: rpc_toolkit::util::Flat(params, Empty {}), - inherited_params, - raw_params, - }) - .await?; - - connect_cli(&ctx, guid).await -} diff --git a/core/startos/src/os_install/mod.rs b/core/startos/src/os_install/mod.rs index c3931b236..b1d9ef1c4 100644 --- a/core/startos/src/os_install/mod.rs +++ b/core/startos/src/os_install/mod.rs @@ -281,6 +281,10 @@ pub async fn execute( IoFormat::Yaml.to_vec(&ServerConfig { os_partitions: Some(part_info.clone()), ethernet_interface: Some(eth_iface), + #[cfg(feature = "dev")] + analytics_url: None, + #[cfg(not(feature = "dev"))] + analytics_url: Some("https://analytics.start9.com".parse()?), // TODO: FullMetal ..Default::default() })?, ) diff --git a/core/startos/src/registry/context.rs b/core/startos/src/registry/context.rs index 99d60307b..0461aa924 100644 --- a/core/startos/src/registry/context.rs +++ b/core/startos/src/registry/context.rs @@ -32,8 +32,8 @@ pub struct RegistryConfig { #[arg(short = 'l', long = "listen")] pub listen: Option, #[arg(short = 'h', long = "hostname")] - pub hostname: InternedString, - #[arg(short = 'p', long = "proxy")] + pub hostname: Option, + #[arg(short = 'p', long = "tor-proxy")] pub tor_proxy: Option, #[arg(short = 'd', long = "datadir")] pub datadir: Option, @@ -43,6 +43,9 @@ impl ContextConfig for RegistryConfig { self.config.take() } fn merge_with(&mut self, other: Self) { + self.listen = self.listen.take().or(other.listen); + self.hostname = self.hostname.take().or(other.hostname); + self.tor_proxy = self.tor_proxy.take().or(other.tor_proxy); self.datadir = self.datadir.take().or(other.datadir); } } @@ -92,7 +95,16 @@ impl RegistryContext { .map(Ok) .unwrap_or_else(|| "socks5h://localhost:9050".parse())?; Ok(Self(Arc::new(RegistryContextSeed { - hostname: config.hostname.clone(), + hostname: config + .hostname + .as_ref() + .ok_or_else(|| { + Error::new( + eyre!("missing required configuration: hostname"), + ErrorKind::NotFound, + ) + })? + .clone(), listen: config .listen .unwrap_or(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 5959)), @@ -169,7 +181,8 @@ impl CallRemote for CliContext { &AnySigningKey::Ed25519(self.developer_key()?.clone()), &body, &host, - )?.to_header(), + )? + .to_header(), ) .body(body) .send() diff --git a/sdk/lib/osBindings/AddAssetParams.ts b/sdk/lib/osBindings/AddAssetParams.ts index ce6128cf7..ffd7db675 100644 --- a/sdk/lib/osBindings/AddAssetParams.ts +++ b/sdk/lib/osBindings/AddAssetParams.ts @@ -6,7 +6,6 @@ import type { Version } from "./Version" export type AddAssetParams = { version: Version platform: string - upload: boolean url: string signature: AnySignature commitment: Blake3Commitment diff --git a/sdk/lib/osBindings/AddPackageParams.ts b/sdk/lib/osBindings/AddPackageParams.ts new file mode 100644 index 000000000..4395b9b8a --- /dev/null +++ b/sdk/lib/osBindings/AddPackageParams.ts @@ -0,0 +1,9 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AnySignature } from "./AnySignature" +import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment" + +export type AddPackageParams = { + url: string + commitment: MerkleArchiveCommitment + signature: AnySignature +} diff --git a/sdk/lib/osBindings/GetPackageResponse.ts b/sdk/lib/osBindings/GetPackageResponse.ts index 5bf24bfc0..3e1dd4e9d 100644 --- a/sdk/lib/osBindings/GetPackageResponse.ts +++ b/sdk/lib/osBindings/GetPackageResponse.ts @@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type GetPackageResponse = { + categories: string[] best: { [key: Version]: PackageVersionInfo } otherVersions?: { [key: Version]: PackageInfoShort } } diff --git a/sdk/lib/osBindings/GetPackageResponseFull.ts b/sdk/lib/osBindings/GetPackageResponseFull.ts index 579924291..e375dd489 100644 --- a/sdk/lib/osBindings/GetPackageResponseFull.ts +++ b/sdk/lib/osBindings/GetPackageResponseFull.ts @@ -3,6 +3,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type GetPackageResponseFull = { + categories: string[] best: { [key: Version]: PackageVersionInfo } otherVersions: { [key: Version]: PackageVersionInfo } } diff --git a/sdk/lib/osBindings/HardwareRequirements.ts b/sdk/lib/osBindings/HardwareRequirements.ts index 4964bc66f..0e1da1f36 100644 --- a/sdk/lib/osBindings/HardwareRequirements.ts +++ b/sdk/lib/osBindings/HardwareRequirements.ts @@ -3,5 +3,5 @@ export type HardwareRequirements = { device: { [key: string]: string } ram: number | null - arch: Array | null + arch: string[] | null } diff --git a/sdk/lib/osBindings/OsVersionInfo.ts b/sdk/lib/osBindings/OsVersionInfo.ts index 7cd0fde9e..a88115350 100644 --- a/sdk/lib/osBindings/OsVersionInfo.ts +++ b/sdk/lib/osBindings/OsVersionInfo.ts @@ -7,7 +7,7 @@ export type OsVersionInfo = { headline: string releaseNotes: string sourceVersion: string - signers: Array + authorized: Array iso: { [key: string]: RegistryAsset } squashfs: { [key: string]: RegistryAsset } img: { [key: string]: RegistryAsset } diff --git a/sdk/lib/osBindings/PackageInfo.ts b/sdk/lib/osBindings/PackageInfo.ts index af340424f..6d07cd43e 100644 --- a/sdk/lib/osBindings/PackageInfo.ts +++ b/sdk/lib/osBindings/PackageInfo.ts @@ -4,6 +4,7 @@ import type { PackageVersionInfo } from "./PackageVersionInfo" import type { Version } from "./Version" export type PackageInfo = { - signers: Array + authorized: Array versions: { [key: Version]: PackageVersionInfo } + categories: string[] } diff --git a/sdk/lib/osBindings/PackageVersionInfo.ts b/sdk/lib/osBindings/PackageVersionInfo.ts index da82540cd..bdded46bd 100644 --- a/sdk/lib/osBindings/PackageVersionInfo.ts +++ b/sdk/lib/osBindings/PackageVersionInfo.ts @@ -17,7 +17,6 @@ export type PackageVersionInfo = { upstreamRepo: string supportSite: string marketingSite: string - categories: string[] osVersion: Version hardwareRequirements: HardwareRequirements sourceVersion: string | null diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index 16fc56a74..3059c6087 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -3,6 +3,7 @@ export { ActionId } from "./ActionId" export { ActionMetadata } from "./ActionMetadata" export { AddAdminParams } from "./AddAdminParams" export { AddAssetParams } from "./AddAssetParams" +export { AddPackageParams } from "./AddPackageParams" export { AddressInfo } from "./AddressInfo" export { AddSslOptions } from "./AddSslOptions" export { AddVersionParams } from "./AddVersionParams" From 8f7072d7e96e4898d5b7b2fe694ee33d0cdf2e42 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Tue, 4 Jun 2024 09:21:55 -0600 Subject: [PATCH 02/20] metrics wip --- core/startos/src/analytics/mod.rs | 69 ++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs index 52ca37948..6903ecfe1 100644 --- a/core/startos/src/analytics/mod.rs +++ b/core/startos/src/analytics/mod.rs @@ -1,19 +1,28 @@ use std::net::SocketAddr; use axum::Router; +use chrono::Utc; use futures::future::ready; -use rpc_toolkit::{Context, ParentHandler, Server}; +use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler, Server}; +use sqlx::query; +use ts_rs::TS; use crate::analytics::context::AnalyticsContext; use crate::middleware::cors::Cors; use crate::net::static_server::{bad_request, not_found, server_error}; use crate::net::web_server::WebServer; use crate::rpc_continuations::Guid; +use crate::Error; pub mod context; pub fn analytics_api() -> ParentHandler { - ParentHandler::new() // TODO: FullMetal + ParentHandler::new() + .subcommand("recordMetrics", from_fn_async(record_metrics).no_cli()) + .subcommand( + "recordUserActivity", + from_fn_async(record_user_activity).no_cli(), + ) } pub fn analytics_server_router(ctx: AnalyticsContext) -> Router { @@ -76,3 +85,59 @@ impl WebServer { Self::new(bind, analytics_server_router(ctx)) } } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +struct MetricsParams { + version: char, + pkg_id: char, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +struct ActivityParams { + server_id: String, + os_version: String, + arch: String, +} + +async fn record_metrics( + ctx: AnalyticsContext, + MetricsParams { + version, + pkg_id, + }: MetricsParams, +) -> Result<(), Error> { + let pool = ctx.db; + let created_at = Utc::now().to_rfc3339(); + query!( + "INSERT INTO metric (created_at, version, pkg_id) VALUES ($1, $2, $3)", + created_at, + version, + pkg_id + ) + .execute(pool) + .await?; + Ok(()) +} + +async fn record_user_activity( + analytics_ctx: AnalyticsContext, + ActivityParams { server_id, os_version, arch }: ActivityParams, +) -> Result<(), Error> { + let pool = analytics_ctx.db; + let created_at = Utc::now().to_rfc3339(); + + query!("INSERT INTO user_activity (created_at, server_id, os_version, arch) VALUES ($1, $2, $3, $4)", + created_at, + server_id, + os_vers, + arch + ) + .execute(pool) + .await?; + + Ok(()) +} From fa347fd49d92a7373f0080243127fc1e87ba8c87 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Tue, 4 Jun 2024 11:53:30 -0600 Subject: [PATCH 03/20] remove record_metrics fn --- core/startos/src/analytics/mod.rs | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs index 6903ecfe1..551082d6b 100644 --- a/core/startos/src/analytics/mod.rs +++ b/core/startos/src/analytics/mod.rs @@ -18,7 +18,6 @@ pub mod context; pub fn analytics_api() -> ParentHandler { ParentHandler::new() - .subcommand("recordMetrics", from_fn_async(record_metrics).no_cli()) .subcommand( "recordUserActivity", from_fn_async(record_user_activity).no_cli(), @@ -86,14 +85,6 @@ impl WebServer { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct MetricsParams { - version: char, - pkg_id: char, -} - #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] @@ -103,26 +94,6 @@ struct ActivityParams { arch: String, } -async fn record_metrics( - ctx: AnalyticsContext, - MetricsParams { - version, - pkg_id, - }: MetricsParams, -) -> Result<(), Error> { - let pool = ctx.db; - let created_at = Utc::now().to_rfc3339(); - query!( - "INSERT INTO metric (created_at, version, pkg_id) VALUES ($1, $2, $3)", - created_at, - version, - pkg_id - ) - .execute(pool) - .await?; - Ok(()) -} - async fn record_user_activity( analytics_ctx: AnalyticsContext, ActivityParams { server_id, os_version, arch }: ActivityParams, From 94875299920e8a54c5b0f45fbbd4adade2bb7a07 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Tue, 4 Jun 2024 11:54:49 -0600 Subject: [PATCH 04/20] remove os version from activity --- core/startos/src/analytics/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs index 551082d6b..1fa08dd3f 100644 --- a/core/startos/src/analytics/mod.rs +++ b/core/startos/src/analytics/mod.rs @@ -90,21 +90,19 @@ impl WebServer { #[serde(rename_all = "camelCase")] struct ActivityParams { server_id: String, - os_version: String, arch: String, } async fn record_user_activity( - analytics_ctx: AnalyticsContext, - ActivityParams { server_id, os_version, arch }: ActivityParams, + ctx: AnalyticsContext, + ActivityParams { server_id, arch }: ActivityParams, ) -> Result<(), Error> { - let pool = analytics_ctx.db; + let pool = ctx.db; let created_at = Utc::now().to_rfc3339(); query!("INSERT INTO user_activity (created_at, server_id, os_version, arch) VALUES ($1, $2, $3, $4)", created_at, server_id, - os_vers, arch ) .execute(pool) From 4afd3c2322ab30ab877c491f08b1c6a4cd23eb51 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Mon, 10 Jun 2024 18:56:39 -0600 Subject: [PATCH 05/20] move MAU tracking back to registry --- core/startos/src/analytics/mod.rs | 36 +-------------------- core/startos/src/registry/context.rs | 12 +++++++ core/startos/src/registry/os/version/mod.rs | 26 ++++++++++++++- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs index 1fa08dd3f..7c84ff386 100644 --- a/core/startos/src/analytics/mod.rs +++ b/core/startos/src/analytics/mod.rs @@ -1,27 +1,19 @@ use std::net::SocketAddr; use axum::Router; -use chrono::Utc; use futures::future::ready; -use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler, Server}; -use sqlx::query; -use ts_rs::TS; +use rpc_toolkit::{Context, ParentHandler, Server}; use crate::analytics::context::AnalyticsContext; use crate::middleware::cors::Cors; use crate::net::static_server::{bad_request, not_found, server_error}; use crate::net::web_server::WebServer; use crate::rpc_continuations::Guid; -use crate::Error; pub mod context; pub fn analytics_api() -> ParentHandler { ParentHandler::new() - .subcommand( - "recordUserActivity", - from_fn_async(record_user_activity).no_cli(), - ) } pub fn analytics_server_router(ctx: AnalyticsContext) -> Router { @@ -84,29 +76,3 @@ impl WebServer { Self::new(bind, analytics_server_router(ctx)) } } - -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -struct ActivityParams { - server_id: String, - arch: String, -} - -async fn record_user_activity( - ctx: AnalyticsContext, - ActivityParams { server_id, arch }: ActivityParams, -) -> Result<(), Error> { - let pool = ctx.db; - let created_at = Utc::now().to_rfc3339(); - - query!("INSERT INTO user_activity (created_at, server_id, os_version, arch) VALUES ($1, $2, $3, $4)", - created_at, - server_id, - arch - ) - .execute(pool) - .await?; - - Ok(()) -} diff --git a/core/startos/src/registry/context.rs b/core/startos/src/registry/context.rs index 0461aa924..64a157073 100644 --- a/core/startos/src/registry/context.rs +++ b/core/startos/src/registry/context.rs @@ -10,6 +10,7 @@ use reqwest::{Client, Proxy}; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{CallRemote, Context, Empty}; use serde::{Deserialize, Serialize}; +use sqlx::PgPool; use tokio::sync::broadcast::Sender; use tracing::instrument; use url::Url; @@ -37,6 +38,8 @@ pub struct RegistryConfig { pub tor_proxy: Option, #[arg(short = 'd', long = "datadir")] pub datadir: Option, + #[arg(short = 'u', long = "pg-connection-url")] + pub pg_connection_url: Option, } impl ContextConfig for RegistryConfig { fn next(&mut self) -> Option { @@ -67,6 +70,7 @@ pub struct RegistryContextSeed { pub rpc_continuations: RpcContinuations, pub client: Client, pub shutdown: Sender<()>, + pub pool: Option, } #[derive(Clone)] @@ -94,6 +98,13 @@ impl RegistryContext { .clone() .map(Ok) .unwrap_or_else(|| "socks5h://localhost:9050".parse())?; + let pool: Option = match &config.pg_connection_url { + Some(url) => match PgPool::connect(url.as_str()).await { + Ok(pool) => Some(pool), + Err(_) => None, + }, + None => None, + }; Ok(Self(Arc::new(RegistryContextSeed { hostname: config .hostname @@ -122,6 +133,7 @@ impl RegistryContext { .build() .with_kind(crate::ErrorKind::ParseUrl)?, shutdown, + pool, }))) } } diff --git a/core/startos/src/registry/os/version/mod.rs b/core/startos/src/registry/os/version/mod.rs index 5bf926e5e..cb36f078a 100644 --- a/core/startos/src/registry/os/version/mod.rs +++ b/core/startos/src/registry/os/version/mod.rs @@ -1,10 +1,12 @@ use std::collections::BTreeMap; +use chrono::Utc; use clap::Parser; use emver::VersionRange; use itertools::Itertools; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; +use sqlx::query; use ts_rs::TS; use crate::context::CliContext; @@ -126,12 +128,34 @@ pub struct GetVersionParams { #[ts(type = "string | null")] #[arg(long = "target")] pub target: Option, + #[ts(type = "string | null")] + #[arg(long = "id")] + server_id: Option, + #[ts(type = "string | null")] + #[arg(long = "arch")] + arch: Option, } pub async fn get_version( ctx: RegistryContext, - GetVersionParams { source, target }: GetVersionParams, + GetVersionParams { + source, + target, + server_id, + arch, + }: GetVersionParams, ) -> Result, Error> { + if let (Some(pool), Some(server_id), Some(arch)) = (ctx.pool, server_id, arch) { + let created_at = Utc::now().to_rfc3339(); + + query!("INSERT INTO user_activity (created_at, server_id, os_version, arch) VALUES ($1, $2, $3, $4)", + created_at, + server_id, + arch + ) + .execute(pool) + .await?; + } let target = target.unwrap_or(VersionRange::Any); ctx.db .peek() From 07104b18f579e96a7fdea27a613355176eb710ca Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Thu, 20 Jun 2024 20:59:16 +0200 Subject: [PATCH 06/20] Update workflows actions (#2628) * Update workflows actions to the latest versions --- .github/workflows/startos-iso.yaml | 20 ++++++++++---------- .github/workflows/test.yaml | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/startos-iso.yaml b/.github/workflows/startos-iso.yaml index 30b2bec0f..c51eafc5c 100644 --- a/.github/workflows/startos-iso.yaml +++ b/.github/workflows/startos-iso.yaml @@ -71,27 +71,27 @@ jobs: sudo mount -t tmpfs tmpfs . if: ${{ github.event.inputs.runner == 'fast' }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ env.NODEJS_VERSION }} - name: Set up docker QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up system dependencies run: sudo apt-get update && sudo apt-get install -y qemu-user-static systemd-container - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Make run: make ARCH=${{ matrix.arch }} compiled-${{ matrix.arch }}.tar - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: compiled-${{ matrix.arch }}.tar path: compiled-${{ matrix.arch }}.tar @@ -144,7 +144,7 @@ jobs: run: rm -rf /opt/hostedtoolcache* if: ${{ github.event.inputs.runner != 'fast' }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -166,7 +166,7 @@ jobs: if: ${{ github.event.inputs.runner == 'fast' && (matrix.platform == 'x86_64' || matrix.platform == 'x86_64-nonfree') }} - name: Download compiled artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: compiled-${{ env.ARCH }}.tar @@ -190,18 +190,18 @@ jobs: run: PLATFORM=${{ matrix.platform }} make img if: ${{ matrix.platform == 'raspberrypi' }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.platform }}.squashfs path: results/*.squashfs - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.platform }}.iso path: results/*.iso if: ${{ matrix.platform != 'raspberrypi' }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.platform }}.img path: results/*.img diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c6082ac25..0a5eb38e9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,11 +19,11 @@ jobs: name: Run Automated Tests runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: ${{ env.NODEJS_VERSION }} From e6abf4e33b841961ff37c701daa2eb4e203d7e9f Mon Sep 17 00:00:00 2001 From: waterplea Date: Fri, 21 Jun 2024 15:51:04 +0500 Subject: [PATCH 07/20] feat: get rid of cyclic dep between patch-db and api service Signed-off-by: waterplea --- web/projects/ui/src/app/app.module.ts | 2 - web/projects/ui/src/app/app.providers.ts | 10 ++++ .../services/api/embassy-live-api.service.ts | 7 ++- .../app/services/patch-db/patch-db-source.ts | 56 +++++++++++++++++++ .../app/services/patch-db/patch-db.factory.ts | 55 ------------------ .../app/services/patch-db/patch-db.module.ts | 20 ------- 6 files changed, 70 insertions(+), 80 deletions(-) create mode 100644 web/projects/ui/src/app/services/patch-db/patch-db-source.ts delete mode 100644 web/projects/ui/src/app/services/patch-db/patch-db.factory.ts delete mode 100644 web/projects/ui/src/app/services/patch-db/patch-db.module.ts diff --git a/web/projects/ui/src/app/app.module.ts b/web/projects/ui/src/app/app.module.ts index c0264f064..f13b4fbfa 100644 --- a/web/projects/ui/src/app/app.module.ts +++ b/web/projects/ui/src/app/app.module.ts @@ -28,7 +28,6 @@ import { PreloaderModule } from './app/preloader/preloader.module' import { FooterModule } from './app/footer/footer.module' import { MenuModule } from './app/menu/menu.module' import { APP_PROVIDERS } from './app.providers' -import { PatchDbModule } from './services/patch-db/patch-db.module' import { ToastContainerModule } from './components/toast-container/toast-container.module' import { ConnectionBarComponentModule } from './components/connection-bar/connection-bar.component.module' import { WidgetsPageModule } from './pages/widgets/widgets.module' @@ -54,7 +53,6 @@ import { environment } from '../environments/environment' MonacoEditorModule, SharedPipesModule, MarketplaceModule, - PatchDbModule, ToastContainerModule, ConnectionBarComponentModule, TuiRootModule, diff --git a/web/projects/ui/src/app/app.providers.ts b/web/projects/ui/src/app/app.providers.ts index bf26a8cb9..c6a8d756e 100644 --- a/web/projects/ui/src/app/app.providers.ts +++ b/web/projects/ui/src/app/app.providers.ts @@ -3,6 +3,11 @@ import { UntypedFormBuilder } from '@angular/forms' import { Router, RouteReuseStrategy } from '@angular/router' import { IonicRouteStrategy, IonNav } from '@ionic/angular' import { RELATIVE_URL, THEME, WorkspaceConfig } from '@start9labs/shared' +import { PatchDB } from 'patch-db-client' +import { + PATCH_CACHE, + PatchDbSource, +} from 'src/app/services/patch-db/patch-db-source' import { ApiService } from './services/api/embassy-api.service' import { MockApiService } from './services/api/embassy-mock-api.service' import { LiveApiService } from './services/api/embassy-live-api.service' @@ -29,6 +34,11 @@ export const APP_PROVIDERS: Provider[] = [ provide: ApiService, useClass: useMocks ? MockApiService : LiveApiService, }, + { + provide: PatchDB, + deps: [PatchDbSource, PATCH_CACHE], + useClass: PatchDB, + }, { provide: APP_INITIALIZER, deps: [StorageService, AuthService, ClientStorageService, Router], diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 8826b2883..954a4475e 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -7,6 +7,7 @@ import { RpcError, RPCOptions, } from '@start9labs/shared' +import { PATCH_CACHE } from 'src/app/services/patch-db/patch-db-source' import { ApiService } from './embassy-api.service' import { RR } from './api.types' import { parsePropertiesPermissive } from 'src/app/util/properties.util' @@ -16,7 +17,7 @@ import { Observable, filter, firstValueFrom } from 'rxjs' import { AuthService } from '../auth.service' import { DOCUMENT } from '@angular/common' import { DataModel } from '../patch-db/data-model' -import { PatchDB, pathFromArray } from 'patch-db-client' +import { Dump, pathFromArray } from 'patch-db-client' @Injectable() export class LiveApiService extends ApiService { @@ -25,7 +26,7 @@ export class LiveApiService extends ApiService { private readonly http: HttpService, private readonly config: ConfigService, private readonly auth: AuthService, - private readonly patch: PatchDB, + @Inject(PATCH_CACHE) private readonly cache$: Observable>, ) { super() ; (window as any).rpcClient = this @@ -482,7 +483,7 @@ export class LiveApiService extends ApiService { const patchSequence = res.headers.get('x-patch-sequence') if (patchSequence) await firstValueFrom( - this.patch.cache$.pipe(filter(({ id }) => id >= Number(patchSequence))), + this.cache$.pipe(filter(({ id }) => id >= Number(patchSequence))), ) return body.result diff --git a/web/projects/ui/src/app/services/patch-db/patch-db-source.ts b/web/projects/ui/src/app/services/patch-db/patch-db-source.ts new file mode 100644 index 000000000..803fbce2c --- /dev/null +++ b/web/projects/ui/src/app/services/patch-db/patch-db-source.ts @@ -0,0 +1,56 @@ +import { inject, Injectable, InjectionToken } from '@angular/core' +import { Dump, Revision, Update } from 'patch-db-client' +import { BehaviorSubject, EMPTY, Observable } from 'rxjs' +import { + bufferTime, + catchError, + filter, + startWith, + switchMap, + take, +} from 'rxjs/operators' +import { StateService } from 'src/app/services/state.service' +import { ApiService } from '../api/embassy-api.service' +import { AuthService } from '../auth.service' +import { DataModel } from './data-model' +import { LocalStorageBootstrap } from './local-storage-bootstrap' + +export const PATCH_CACHE = new InjectionToken('', { + factory: () => + new BehaviorSubject>({ + id: 0, + value: {} as DataModel, + }), +}) + +@Injectable({ + providedIn: 'root', +}) +export class PatchDbSource extends Observable[]> { + private readonly api = inject(ApiService) + private readonly state = inject(StateService) + private readonly stream$ = inject(AuthService).isVerified$.pipe( + switchMap(verified => (verified ? this.api.subscribeToPatchDB({}) : EMPTY)), + switchMap(({ dump, guid }) => + this.api.openWebsocket$(guid, {}).pipe( + bufferTime(250), + filter(revisions => !!revisions.length), + startWith([dump]), + ), + ), + catchError((_, original$) => { + this.state.retrigger() + + return this.state.pipe( + filter(current => current === 'running'), + take(1), + switchMap(() => original$), + ) + }), + startWith([inject(LocalStorageBootstrap).init()]), + ) + + constructor() { + super(subscriber => this.stream$.subscribe(subscriber)) + } +} diff --git a/web/projects/ui/src/app/services/patch-db/patch-db.factory.ts b/web/projects/ui/src/app/services/patch-db/patch-db.factory.ts deleted file mode 100644 index bb2dcdf57..000000000 --- a/web/projects/ui/src/app/services/patch-db/patch-db.factory.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { InjectionToken, Injector } from '@angular/core' -import { Revision, Update } from 'patch-db-client' -import { defer, EMPTY, from, Observable } from 'rxjs' -import { - bufferTime, - catchError, - filter, - startWith, - switchMap, - take, -} from 'rxjs/operators' -import { StateService } from 'src/app/services/state.service' -import { ApiService } from '../api/embassy-api.service' -import { AuthService } from '../auth.service' -import { DataModel } from './data-model' -import { LocalStorageBootstrap } from './local-storage-bootstrap' - -export const PATCH_SOURCE = new InjectionToken[]>>( - '', -) - -export function sourceFactory( - injector: Injector, -): Observable[]> { - // defer() needed to avoid circular dependency with ApiService, since PatchDB is needed there - return defer(() => { - const api = injector.get(ApiService) - const auth = injector.get(AuthService) - const state = injector.get(StateService) - const bootstrapper = injector.get(LocalStorageBootstrap) - - return auth.isVerified$.pipe( - switchMap(verified => - verified ? from(api.subscribeToPatchDB({})) : EMPTY, - ), - switchMap(({ dump, guid }) => - api.openWebsocket$(guid, {}).pipe( - bufferTime(250), - filter(revisions => !!revisions.length), - startWith([dump]), - ), - ), - catchError((_, original$) => { - state.retrigger() - - return state.pipe( - filter(current => current === 'running'), - take(1), - switchMap(() => original$), - ) - }), - startWith([bootstrapper.init()]), - ) - }) -} diff --git a/web/projects/ui/src/app/services/patch-db/patch-db.module.ts b/web/projects/ui/src/app/services/patch-db/patch-db.module.ts deleted file mode 100644 index 3c816e339..000000000 --- a/web/projects/ui/src/app/services/patch-db/patch-db.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PatchDB } from 'patch-db-client' -import { Injector, NgModule } from '@angular/core' -import { PATCH_SOURCE, sourceFactory } from './patch-db.factory' - -// This module is purely for providers organization purposes -@NgModule({ - providers: [ - { - provide: PATCH_SOURCE, - deps: [Injector], - useFactory: sourceFactory, - }, - { - provide: PatchDB, - deps: [PATCH_SOURCE], - useClass: PatchDB, - }, - ], -}) -export class PatchDbModule {} From 133dfd5063a36b8db2b5bd1b54a20a3e462f4082 Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Fri, 21 Jun 2024 18:39:05 -0600 Subject: [PATCH 08/20] match query to registry table --- core/startos/src/registry/os/version/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/startos/src/registry/os/version/mod.rs b/core/startos/src/registry/os/version/mod.rs index cb36f078a..234143295 100644 --- a/core/startos/src/registry/os/version/mod.rs +++ b/core/startos/src/registry/os/version/mod.rs @@ -145,10 +145,10 @@ pub async fn get_version( arch, }: GetVersionParams, ) -> Result, Error> { - if let (Some(pool), Some(server_id), Some(arch)) = (ctx.pool, server_id, arch) { - let created_at = Utc::now().to_rfc3339(); + if let (Some(pool), Some(server_id), Some(arch)) = (&ctx.pool, server_id, arch) { + let created_at = Utc::now(); - query!("INSERT INTO user_activity (created_at, server_id, os_version, arch) VALUES ($1, $2, $3, $4)", + query!("INSERT INTO user_activity (created_at, server_id, arch) VALUES ($1, $2, $3)", created_at, server_id, arch From b0c0cd7fda2400712fc928a25da5e4cbd42031cf Mon Sep 17 00:00:00 2001 From: Shadowy Super Coder Date: Fri, 21 Jun 2024 18:40:32 -0600 Subject: [PATCH 09/20] add script to cache registry db --- ...3452e7a69768da179e78479cd35ee42b493ae.json | 16 + .../os/version/db/registry-sqlx-data.sh | 27 + .../os/version/db/registry_schema.sql | 828 ++++++++++++++++++ 3 files changed, 871 insertions(+) create mode 100644 core/startos/.sqlx/query-bc9382d34bf93f468c64d0d02613452e7a69768da179e78479cd35ee42b493ae.json create mode 100755 core/startos/src/registry/os/version/db/registry-sqlx-data.sh create mode 100644 core/startos/src/registry/os/version/db/registry_schema.sql diff --git a/core/startos/.sqlx/query-bc9382d34bf93f468c64d0d02613452e7a69768da179e78479cd35ee42b493ae.json b/core/startos/.sqlx/query-bc9382d34bf93f468c64d0d02613452e7a69768da179e78479cd35ee42b493ae.json new file mode 100644 index 000000000..d5fae12b7 --- /dev/null +++ b/core/startos/.sqlx/query-bc9382d34bf93f468c64d0d02613452e7a69768da179e78479cd35ee42b493ae.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO user_activity (created_at, server_id, arch) VALUES ($1, $2, $3)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Timestamptz", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "bc9382d34bf93f468c64d0d02613452e7a69768da179e78479cd35ee42b493ae" +} diff --git a/core/startos/src/registry/os/version/db/registry-sqlx-data.sh b/core/startos/src/registry/os/version/db/registry-sqlx-data.sh new file mode 100755 index 000000000..2a422bf38 --- /dev/null +++ b/core/startos/src/registry/os/version/db/registry-sqlx-data.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +TMP_DIR=$(mktemp -d) +mkdir $TMP_DIR/pgdata +docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR/pgdata:/var/lib/postgresql/data postgres + +( + set -e + ctr=0 + until docker exec tmp_postgres psql -U postgres || [ $ctr -ge 5 ]; do + ctr=$[ctr + 1] + sleep 5; + done + + PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres) + + SCHEMA_DUMP="registry_schema.sql" + + DATABASE_URL=postgres://postgres:password@$PG_IP/postgres + psql $DATABASE_URL -f "$SCRIPT_DIR/$SCHEMA_DUMP" + DATABASE_URL=postgres://postgres:password@$PG_IP/postgres PLATFORM=$(uname -m) cargo sqlx prepare -- --lib --profile=test --workspace + echo "Subscript Complete" +) + +docker stop tmp_postgres +sudo rm -rf $TMP_DIR diff --git a/core/startos/src/registry/os/version/db/registry_schema.sql b/core/startos/src/registry/os/version/db/registry_schema.sql new file mode 100644 index 000000000..abd9f4ea6 --- /dev/null +++ b/core/startos/src/registry/os/version/db/registry_schema.sql @@ -0,0 +1,828 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 14.12 (Ubuntu 14.12-0ubuntu0.22.04.1) +-- Dumped by pg_dump version 14.12 (Ubuntu 14.12-0ubuntu0.22.04.1) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: admin; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.admin ( + id character varying NOT NULL, + created_at timestamp with time zone NOT NULL, + pass_hash character varying NOT NULL, + deleted_at timestamp with time zone +); + + +ALTER TABLE public.admin OWNER TO alpha_admin; + +-- +-- Name: admin_pkgs; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.admin_pkgs ( + id bigint NOT NULL, + admin character varying NOT NULL, + pkg_id character varying NOT NULL +); + + +ALTER TABLE public.admin_pkgs OWNER TO alpha_admin; + +-- +-- Name: admin_pkgs_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.admin_pkgs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.admin_pkgs_id_seq OWNER TO alpha_admin; + +-- +-- Name: admin_pkgs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.admin_pkgs_id_seq OWNED BY public.admin_pkgs.id; + + +-- +-- Name: category; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.category ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + name character varying NOT NULL, + description character varying NOT NULL, + priority bigint DEFAULT 0 NOT NULL +); + + +ALTER TABLE public.category OWNER TO alpha_admin; + +-- +-- Name: category_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.category_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.category_id_seq OWNER TO alpha_admin; + +-- +-- Name: category_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.category_id_seq OWNED BY public.category.id; + + +-- +-- Name: eos_hash; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.eos_hash ( + id bigint NOT NULL, + version character varying NOT NULL, + hash character varying NOT NULL +); + + +ALTER TABLE public.eos_hash OWNER TO alpha_admin; + +-- +-- Name: eos_hash_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.eos_hash_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.eos_hash_id_seq OWNER TO alpha_admin; + +-- +-- Name: eos_hash_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.eos_hash_id_seq OWNED BY public.eos_hash.id; + + +-- +-- Name: error_log_record; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.error_log_record ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + epoch character varying NOT NULL, + commit_hash character varying NOT NULL, + source_file character varying NOT NULL, + line bigint NOT NULL, + target character varying NOT NULL, + level character varying NOT NULL, + message character varying NOT NULL, + incidents bigint NOT NULL +); + + +ALTER TABLE public.error_log_record OWNER TO alpha_admin; + +-- +-- Name: error_log_record_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.error_log_record_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.error_log_record_id_seq OWNER TO alpha_admin; + +-- +-- Name: error_log_record_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.error_log_record_id_seq OWNED BY public.error_log_record.id; + + +-- +-- Name: metric; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.metric ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + version character varying NOT NULL, + pkg_id character varying NOT NULL +); + + +ALTER TABLE public.metric OWNER TO alpha_admin; + +-- +-- Name: metric_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.metric_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.metric_id_seq OWNER TO alpha_admin; + +-- +-- Name: metric_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.metric_id_seq OWNED BY public.metric.id; + + +-- +-- Name: os_version; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.os_version ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + number character varying NOT NULL, + headline character varying NOT NULL, + release_notes character varying NOT NULL, + arch character varying +); + + +ALTER TABLE public.os_version OWNER TO alpha_admin; + +-- +-- Name: os_version_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.os_version_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.os_version_id_seq OWNER TO alpha_admin; + +-- +-- Name: os_version_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.os_version_id_seq OWNED BY public.os_version.id; + + +-- +-- Name: persistent_migration; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.persistent_migration ( + id integer NOT NULL, + version integer NOT NULL, + label character varying, + "timestamp" timestamp with time zone NOT NULL +); + + +ALTER TABLE public.persistent_migration OWNER TO alpha_admin; + +-- +-- Name: persistent_migration_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.persistent_migration_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.persistent_migration_id_seq OWNER TO alpha_admin; + +-- +-- Name: persistent_migration_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.persistent_migration_id_seq OWNED BY public.persistent_migration.id; + + +-- +-- Name: pkg_category; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.pkg_category ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + category_id bigint NOT NULL, + pkg_id character varying NOT NULL +); + + +ALTER TABLE public.pkg_category OWNER TO alpha_admin; + +-- +-- Name: pkg_dependency; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.pkg_dependency ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + pkg_id character varying NOT NULL, + pkg_version character varying NOT NULL, + dep_id character varying NOT NULL, + dep_version_range character varying NOT NULL +); + + +ALTER TABLE public.pkg_dependency OWNER TO alpha_admin; + +-- +-- Name: pkg_dependency_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.pkg_dependency_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.pkg_dependency_id_seq OWNER TO alpha_admin; + +-- +-- Name: pkg_dependency_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.pkg_dependency_id_seq OWNED BY public.pkg_dependency.id; + + +-- +-- Name: pkg_record; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.pkg_record ( + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone, + pkg_id character varying NOT NULL, + hidden boolean DEFAULT false NOT NULL +); + + +ALTER TABLE public.pkg_record OWNER TO alpha_admin; + +-- +-- Name: service_category_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.service_category_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.service_category_id_seq OWNER TO alpha_admin; + +-- +-- Name: service_category_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.service_category_id_seq OWNED BY public.pkg_category.id; + + +-- +-- Name: upload; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.upload ( + id bigint NOT NULL, + uploader character varying NOT NULL, + pkg_id character varying NOT NULL, + pkg_version character varying NOT NULL, + created_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.upload OWNER TO alpha_admin; + +-- +-- Name: upload_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.upload_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.upload_id_seq OWNER TO alpha_admin; + +-- +-- Name: upload_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.upload_id_seq OWNED BY public.upload.id; + + +-- +-- Name: user_activity; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.user_activity ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + server_id character varying NOT NULL, + os_version character varying, + arch character varying +); + + +ALTER TABLE public.user_activity OWNER TO alpha_admin; + +-- +-- Name: user_activity_id_seq; Type: SEQUENCE; Schema: public; Owner: alpha_admin +-- + +CREATE SEQUENCE public.user_activity_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.user_activity_id_seq OWNER TO alpha_admin; + +-- +-- Name: user_activity_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: alpha_admin +-- + +ALTER SEQUENCE public.user_activity_id_seq OWNED BY public.user_activity.id; + + +-- +-- Name: version; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.version ( + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone, + number character varying NOT NULL, + release_notes character varying NOT NULL, + os_version character varying NOT NULL, + pkg_id character varying NOT NULL, + title character varying NOT NULL, + desc_short character varying NOT NULL, + desc_long character varying NOT NULL, + icon_type character varying NOT NULL, + deprecated_at timestamp with time zone +); + + +ALTER TABLE public.version OWNER TO alpha_admin; + +-- +-- Name: version_platform; Type: TABLE; Schema: public; Owner: alpha_admin +-- + +CREATE TABLE public.version_platform ( + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone, + pkg_id character varying NOT NULL, + version_number character varying NOT NULL, + arch character varying NOT NULL, + ram bigint, + device jsonb +); + + +ALTER TABLE public.version_platform OWNER TO alpha_admin; + +-- +-- Name: admin_pkgs id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.admin_pkgs ALTER COLUMN id SET DEFAULT nextval('public.admin_pkgs_id_seq'::regclass); + + +-- +-- Name: category id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.category ALTER COLUMN id SET DEFAULT nextval('public.category_id_seq'::regclass); + + +-- +-- Name: eos_hash id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.eos_hash ALTER COLUMN id SET DEFAULT nextval('public.eos_hash_id_seq'::regclass); + + +-- +-- Name: error_log_record id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.error_log_record ALTER COLUMN id SET DEFAULT nextval('public.error_log_record_id_seq'::regclass); + + +-- +-- Name: metric id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.metric ALTER COLUMN id SET DEFAULT nextval('public.metric_id_seq'::regclass); + + +-- +-- Name: os_version id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.os_version ALTER COLUMN id SET DEFAULT nextval('public.os_version_id_seq'::regclass); + + +-- +-- Name: persistent_migration id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.persistent_migration ALTER COLUMN id SET DEFAULT nextval('public.persistent_migration_id_seq'::regclass); + + +-- +-- Name: pkg_category id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_category ALTER COLUMN id SET DEFAULT nextval('public.service_category_id_seq'::regclass); + + +-- +-- Name: pkg_dependency id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_dependency ALTER COLUMN id SET DEFAULT nextval('public.pkg_dependency_id_seq'::regclass); + + +-- +-- Name: upload id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.upload ALTER COLUMN id SET DEFAULT nextval('public.upload_id_seq'::regclass); + + +-- +-- Name: user_activity id; Type: DEFAULT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.user_activity ALTER COLUMN id SET DEFAULT nextval('public.user_activity_id_seq'::regclass); + + +-- +-- Name: admin admin_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.admin + ADD CONSTRAINT admin_pkey PRIMARY KEY (id); + + +-- +-- Name: admin_pkgs admin_pkgs_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.admin_pkgs + ADD CONSTRAINT admin_pkgs_pkey PRIMARY KEY (id); + + +-- +-- Name: category category_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.category + ADD CONSTRAINT category_pkey PRIMARY KEY (id); + + +-- +-- Name: eos_hash eos_hash_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.eos_hash + ADD CONSTRAINT eos_hash_pkey PRIMARY KEY (id); + + +-- +-- Name: error_log_record error_log_record_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.error_log_record + ADD CONSTRAINT error_log_record_pkey PRIMARY KEY (id); + + +-- +-- Name: metric metric_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.metric + ADD CONSTRAINT metric_pkey PRIMARY KEY (id); + + +-- +-- Name: os_version os_version_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.os_version + ADD CONSTRAINT os_version_pkey PRIMARY KEY (id); + + +-- +-- Name: persistent_migration persistent_migration_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.persistent_migration + ADD CONSTRAINT persistent_migration_pkey PRIMARY KEY (id); + + +-- +-- Name: pkg_category pkg_category_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_category + ADD CONSTRAINT pkg_category_pkey PRIMARY KEY (id); + + +-- +-- Name: pkg_dependency pkg_dependency_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_dependency + ADD CONSTRAINT pkg_dependency_pkey PRIMARY KEY (id); + + +-- +-- Name: admin_pkgs unique_admin_pkg; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.admin_pkgs + ADD CONSTRAINT unique_admin_pkg UNIQUE (pkg_id, admin); + + +-- +-- Name: error_log_record unique_log_record; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.error_log_record + ADD CONSTRAINT unique_log_record UNIQUE (epoch, commit_hash, source_file, line, target, level, message); + + +-- +-- Name: category unique_name; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.category + ADD CONSTRAINT unique_name UNIQUE (name); + + +-- +-- Name: pkg_category unique_pkg_category; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_category + ADD CONSTRAINT unique_pkg_category UNIQUE (pkg_id, category_id); + + +-- +-- Name: pkg_dependency unique_pkg_dep_version; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_dependency + ADD CONSTRAINT unique_pkg_dep_version UNIQUE (pkg_id, pkg_version, dep_id); + + +-- +-- Name: eos_hash unique_version; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.eos_hash + ADD CONSTRAINT unique_version UNIQUE (version); + + +-- +-- Name: upload upload_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.upload + ADD CONSTRAINT upload_pkey PRIMARY KEY (id); + + +-- +-- Name: user_activity user_activity_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.user_activity + ADD CONSTRAINT user_activity_pkey PRIMARY KEY (id); + + +-- +-- Name: version version_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.version + ADD CONSTRAINT version_pkey PRIMARY KEY (pkg_id, number); + + +-- +-- Name: version_platform version_platform_pkey; Type: CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.version_platform + ADD CONSTRAINT version_platform_pkey PRIMARY KEY (pkg_id, version_number, arch); + + +-- +-- Name: category_name_idx; Type: INDEX; Schema: public; Owner: alpha_admin +-- + +CREATE UNIQUE INDEX category_name_idx ON public.category USING btree (name); + + +-- +-- Name: pkg_record_pkg_id_idx; Type: INDEX; Schema: public; Owner: alpha_admin +-- + +CREATE UNIQUE INDEX pkg_record_pkg_id_idx ON public.pkg_record USING btree (pkg_id); + + +-- +-- Name: version_number_idx; Type: INDEX; Schema: public; Owner: alpha_admin +-- + +CREATE INDEX version_number_idx ON public.version USING btree (number); + + +-- +-- Name: admin_pkgs admin_pkgs_admin_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.admin_pkgs + ADD CONSTRAINT admin_pkgs_admin_fkey FOREIGN KEY (admin) REFERENCES public.admin(id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: metric metric_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.metric + ADD CONSTRAINT metric_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: pkg_category pkg_category_category_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_category + ADD CONSTRAINT pkg_category_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.category(id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: pkg_category pkg_category_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_category + ADD CONSTRAINT pkg_category_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: pkg_dependency pkg_dependency_dep_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_dependency + ADD CONSTRAINT pkg_dependency_dep_id_fkey FOREIGN KEY (dep_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: pkg_dependency pkg_dependency_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.pkg_dependency + ADD CONSTRAINT pkg_dependency_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: upload upload_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.upload + ADD CONSTRAINT upload_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: upload upload_uploader_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.upload + ADD CONSTRAINT upload_uploader_fkey FOREIGN KEY (uploader) REFERENCES public.admin(id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: version version_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.version + ADD CONSTRAINT version_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- Name: version_platform version_platform_pkg_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: alpha_admin +-- + +ALTER TABLE ONLY public.version_platform + ADD CONSTRAINT version_platform_pkg_id_fkey FOREIGN KEY (pkg_id) REFERENCES public.pkg_record(pkg_id) ON UPDATE RESTRICT ON DELETE RESTRICT; + + +-- +-- PostgreSQL database dump complete +-- + From e0d23f4436cb7bc28106702efaf45a5c581fca9e Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 22 Jun 2024 11:33:30 -0600 Subject: [PATCH 10/20] bump patchDB dep --- patch-db | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patch-db b/patch-db index 7aa53249f..c537a07ea 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 7aa53249f9353162475ea347abac92abcfba5493 +Subproject commit c537a07ea937e69b66841d903c70fd75623e5457 From 68ed1c80ce5edf546081e86e9cb62309fff4112a Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 22 Jun 2024 21:47:18 -0600 Subject: [PATCH 11/20] update todos --- web/projects/ui/src/app/app.providers.ts | 2 +- .../ui/src/app/modals/generic-form/generic-form.page.ts | 1 - .../pages/apps-routes/app-actions/app-actions.page.ts | 9 ++------- web/projects/ui/src/app/pages/init/init.service.ts | 1 + web/projects/ui/src/app/services/api/api.fixures.ts | 6 +++--- web/projects/ui/src/app/services/api/mock-patch.ts | 2 +- .../ui/src/app/services/patch-db/patch-db-source.ts | 1 + 7 files changed, 9 insertions(+), 13 deletions(-) diff --git a/web/projects/ui/src/app/app.providers.ts b/web/projects/ui/src/app/app.providers.ts index c6a8d756e..e93c323d0 100644 --- a/web/projects/ui/src/app/app.providers.ts +++ b/web/projects/ui/src/app/app.providers.ts @@ -64,7 +64,7 @@ export function appInitializer( return () => { storage.migrate036() auth.init() - localStorage.init() // @TODO pretty sure we can navigate before this step + localStorage.init() router.initialNavigation() } } diff --git a/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts b/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts index eb690a787..e74c65d47 100644 --- a/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts +++ b/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts @@ -54,7 +54,6 @@ export class GenericFormPage { return } - // @TODO make this more like generic input component dismissal const success = await handler(this.formGroup.value) if (success !== false) this.modalCtrl.dismiss() } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index a48ac8185..fc008a17a 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -44,12 +44,7 @@ export class AppActionsPage { status: T.Status, action: { key: string; value: T.ActionMetadata }, ) { - if ( - status && - action.value.allowedStatuses.includes( - status.main.status, // @TODO - ) - ) { + if (status && action.value.allowedStatuses.includes(status.main.status)) { if (!isEmptyObject(action.value.input || {})) { const modal = await this.modalCtrl.create({ component: GenericFormPage, @@ -91,7 +86,7 @@ export class AppActionsPage { await alert.present() } } else { - const statuses = [...action.value.allowedStatuses] // @TODO + const statuses = [...action.value.allowedStatuses] const last = statuses.pop() let statusesStr = statuses.join(', ') let error = '' diff --git a/web/projects/ui/src/app/pages/init/init.service.ts b/web/projects/ui/src/app/pages/init/init.service.ts index 3cca42a58..7102c9db5 100644 --- a/web/projects/ui/src/app/pages/init/init.service.ts +++ b/web/projects/ui/src/app/pages/init/init.service.ts @@ -58,6 +58,7 @@ export class InitService extends Observable { } }), catchError(e => { + // @TODO this toast is presenting when we navigate away from init page. It seems other websockets exhibit the same behavior, but we never noticed because the error were not being caught and presented in this manner this.errorService.present(e) return EMPTY diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index 38ee4774b..acebdfdfb 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -714,7 +714,7 @@ export module Mock { value: 'https://guessagain.com', }, }, - } as any // @TODO why is this necessary? + } export const ConfigSpec: RR.GetPackageConfigRes['spec'] = { bitcoin: { @@ -1419,7 +1419,7 @@ export module Mock { health: {}, }, }, - actions: {}, // @TODO need mocks + actions: {}, serviceInterfaces: { ui: { id: 'ui', @@ -1618,7 +1618,7 @@ export module Mock { icon: 'assets/img/service-icons/btc-rpc-proxy.png', kind: 'exists', registryUrl: 'https://community-registry.start9.com', - versionSpec: '>2.0.0', // @TODO + versionSpec: '>2.0.0', configSatisfied: false, }, }, diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index ee96f9fe2..755799522 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -127,7 +127,7 @@ export const mockPatchData: DataModel = { }, }, }, - actions: {}, // @TODO + actions: {}, serviceInterfaces: { ui: { id: 'ui', diff --git a/web/projects/ui/src/app/services/patch-db/patch-db-source.ts b/web/projects/ui/src/app/services/patch-db/patch-db-source.ts index 803fbce2c..0b80370d0 100644 --- a/web/projects/ui/src/app/services/patch-db/patch-db-source.ts +++ b/web/projects/ui/src/app/services/patch-db/patch-db-source.ts @@ -41,6 +41,7 @@ export class PatchDbSource extends Observable[]> { catchError((_, original$) => { this.state.retrigger() + // @TODO this is returning right away, but we need to wait until state emits again from the retrigger() above. return this.state.pipe( filter(current => current === 'running'), take(1), From 2c255b6dfefb31e9ef9187cf8f54b8240a176049 Mon Sep 17 00:00:00 2001 From: Jade <2364004+Blu-J@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:00:31 -0600 Subject: [PATCH 12/20] chore: Do some type cleanups (#2650) chore: fix the WithProcedureId --- .../src/service/service_effect_handler.rs | 124 ++++++++---------- sdk/lib/mainFn/Daemons.ts | 6 +- sdk/lib/osBindings/ExecuteAction.ts | 2 + sdk/lib/osBindings/ProcedureId.ts | 4 + sdk/lib/osBindings/SetDependenciesParams.ts | 2 + sdk/lib/osBindings/SetMainStatusStatus.ts | 2 +- sdk/lib/osBindings/index.ts | 1 + 7 files changed, 69 insertions(+), 72 deletions(-) create mode 100644 sdk/lib/osBindings/ProcedureId.ts diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index bcd6da7d1..a61a00b9b 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -64,56 +64,6 @@ impl EffectContext { } } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct WithProcedureId { - #[serde(default)] - procedure_id: Guid, - #[serde(flatten)] - rest: T, -} -impl FromArgMatches for WithProcedureId { - fn from_arg_matches(matches: &clap::ArgMatches) -> Result { - let rest = T::from_arg_matches(matches)?; - Ok(Self { - procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), - rest, - }) - } - fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { - let rest = T::from_arg_matches_mut(matches)?; - Ok(Self { - procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), - rest, - }) - } - fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { - self.rest.update_from_arg_matches(matches)?; - self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); - Ok(()) - } - fn update_from_arg_matches_mut( - &mut self, - matches: &mut clap::ArgMatches, - ) -> Result<(), clap::Error> { - self.rest.update_from_arg_matches_mut(matches)?; - self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); - Ok(()) - } -} -impl CommandFactory for WithProcedureId { - fn command() -> clap::Command { - T::command_for_update().arg( - clap::Arg::new("procedure-id") - .action(clap::ArgAction::Set) - .value_parser(clap::value_parser!(Guid)), - ) - } - fn command_for_update() -> clap::Command { - Self::command() - } -} - pub fn service_effect_handler() -> ParentHandler { ParentHandler::new() .subcommand("gitInfo", from_fn(|_: C| crate::version::git_info())) @@ -877,6 +827,8 @@ async fn exists(context: EffectContext, params: ParamsPackageId) -> Result, #[ts(type = "string")] @@ -886,15 +838,12 @@ struct ExecuteAction { } async fn execute_action( context: EffectContext, - WithProcedureId { + ExecuteAction { procedure_id, - rest: - ExecuteAction { - service_id, - action_id, - input, - }, - }: WithProcedureId, + service_id, + action_id, + input, + }: ExecuteAction, ) -> Result { let context = context.deref()?; let package_id = service_id @@ -950,9 +899,53 @@ async fn running(context: EffectContext, params: ParamsPackageId) -> Result Result { + Ok(Self { + procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), + }) + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { + Ok(Self { + procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), + }) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); + Ok(()) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> Result<(), clap::Error> { + self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); + Ok(()) + } +} +impl CommandFactory for ProcedureId { + fn command() -> clap::Command { + Self::command_for_update().arg( + clap::Arg::new("procedure-id") + .action(clap::ArgAction::Set) + .value_parser(clap::value_parser!(Guid)), + ) + } + fn command_for_update() -> clap::Command { + Self::command() + } +} + async fn restart( context: EffectContext, - WithProcedureId { procedure_id, .. }: WithProcedureId, + ProcedureId { procedure_id }: ProcedureId, ) -> Result<(), Error> { let context = context.deref()?; context.restart(procedure_id).await?; @@ -961,7 +954,7 @@ async fn restart( async fn shutdown( context: EffectContext, - WithProcedureId { procedure_id, .. }: WithProcedureId, + ProcedureId { procedure_id }: ProcedureId, ) -> Result<(), Error> { let context = context.deref()?; context.stop(procedure_id).await?; @@ -1001,7 +994,6 @@ async fn set_configured(context: EffectContext, params: SetConfigured) -> Result enum SetMainStatusStatus { Running, Stopped, - Starting, } impl FromStr for SetMainStatusStatus { type Err = color_eyre::eyre::Report; @@ -1009,7 +1001,6 @@ impl FromStr for SetMainStatusStatus { match s { "running" => Ok(Self::Running), "stopped" => Ok(Self::Stopped), - "starting" => Ok(Self::Starting), _ => Err(eyre!("unknown status {s}")), } } @@ -1033,7 +1024,6 @@ async fn set_main_status(context: EffectContext, params: SetMainStatus) -> Resul match params.status { SetMainStatusStatus::Running => context.seed.started(), SetMainStatusStatus::Stopped => context.seed.stopped(), - SetMainStatusStatus::Starting => context.seed.started(), } Ok(Value::Null) } @@ -1262,15 +1252,17 @@ impl ValueParserFactory for DependencyRequirement { #[command(rename_all = "camelCase")] #[ts(export)] struct SetDependenciesParams { + #[serde(default)] + procedure_id: Guid, dependencies: Vec, } async fn set_dependencies( context: EffectContext, - WithProcedureId { + SetDependenciesParams { procedure_id, - rest: SetDependenciesParams { dependencies }, - }: WithProcedureId, + dependencies, + }: SetDependenciesParams, ) -> Result<(), Error> { let context = context.deref()?; let id = &context.seed.id; diff --git a/sdk/lib/mainFn/Daemons.ts b/sdk/lib/mainFn/Daemons.ts index f48dd51c0..059d148ab 100644 --- a/sdk/lib/mainFn/Daemons.ts +++ b/sdk/lib/mainFn/Daemons.ts @@ -168,10 +168,6 @@ export class Daemons { } private updateMainHealth() { - if (this.healthDaemons.every((x) => x.health.status === "success")) { - this.effects.setMainStatus({ status: "running" }) - } else { - this.effects.setMainStatus({ status: "starting" }) - } + this.effects.setMainStatus({ status: "running" }) } } diff --git a/sdk/lib/osBindings/ExecuteAction.ts b/sdk/lib/osBindings/ExecuteAction.ts index abd8c151f..b4eb60949 100644 --- a/sdk/lib/osBindings/ExecuteAction.ts +++ b/sdk/lib/osBindings/ExecuteAction.ts @@ -1,6 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Guid } from "./Guid" export type ExecuteAction = { + procedureId: Guid serviceId: string | null actionId: string input: any diff --git a/sdk/lib/osBindings/ProcedureId.ts b/sdk/lib/osBindings/ProcedureId.ts new file mode 100644 index 000000000..4d9a0debd --- /dev/null +++ b/sdk/lib/osBindings/ProcedureId.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { Guid } from "./Guid" + +export type ProcedureId = { procedureId: Guid } diff --git a/sdk/lib/osBindings/SetDependenciesParams.ts b/sdk/lib/osBindings/SetDependenciesParams.ts index 7b34b50c9..bbc9b325f 100644 --- a/sdk/lib/osBindings/SetDependenciesParams.ts +++ b/sdk/lib/osBindings/SetDependenciesParams.ts @@ -1,6 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { DependencyRequirement } from "./DependencyRequirement" +import type { Guid } from "./Guid" export type SetDependenciesParams = { + procedureId: Guid dependencies: Array } diff --git a/sdk/lib/osBindings/SetMainStatusStatus.ts b/sdk/lib/osBindings/SetMainStatusStatus.ts index 6db32e7bf..03bb4a119 100644 --- a/sdk/lib/osBindings/SetMainStatusStatus.ts +++ b/sdk/lib/osBindings/SetMainStatusStatus.ts @@ -1,3 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type SetMainStatusStatus = "running" | "stopped" | "starting" +export type SetMainStatusStatus = "running" | "stopped" diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index ac2e19e45..22b03a7ca 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -108,6 +108,7 @@ export { PackageVersionInfo } from "./PackageVersionInfo" export { ParamsMaybePackageId } from "./ParamsMaybePackageId" export { ParamsPackageId } from "./ParamsPackageId" export { PasswordType } from "./PasswordType" +export { ProcedureId } from "./ProcedureId" export { Progress } from "./Progress" export { Public } from "./Public" export { RecoverySource } from "./RecoverySource" From 00f7fa507b1560393366eb6d29867ad855efcd9c Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 24 Jun 2024 16:15:32 -0600 Subject: [PATCH 13/20] remove analyticsd, clean up script --- core/build-analyticsd.sh | 42 ------ ...00f2b22433c86617a0dbf796bf2170186dd2e.json | 16 --- ...ea745ffa5da4479478c1fd2158a45324b1930.json | 14 -- ...36e463c20c841a7d6a0eb15be0f24f4a928ec.json | 40 ------ ...b8c606ca400e43e31b9a05d2937217e0f6962.json | 15 --- ...ef6cb0f1e9a4e158260700f1639dd4b438997.json | 34 ----- ...4f925f61dc5ea371903e62cdffa5d7b67ca96.json | 50 -------- ...c3afb81c2be5c681cfa8b4be0ce70610e9c3a.json | 14 -- ...a83dbc6afd07cae69d246987f62cf0cc35c2a.json | 20 --- ...bd8361776290a9411d527eaf1fdb40bef399d.json | 23 ---- ...9c1550a41ee580dad3f49d35cb843ebef10ca.json | 14 -- ...5b987c5af5807151ff71a59000014586752e0.json | 24 ---- ...d514a66c421a11ab04c26d89a7aa8f6b67210.json | 65 ---------- ...b98a73045adf618696cd838d79203ef5383fb.json | 19 --- ...9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json | 14 -- ...e3526b70bccf3e407327917294a993bc17ed5.json | 16 --- ...bbf6bea226d38efaf6559923c79a36d5ca08c.json | 64 --------- ...5ab58252362694cf0f56999bf60194d20583a.json | 44 ------- ...337527233c84eee758ac9be967906e5841d27.json | 14 -- ...9424e6b5a1d1402e028219e21726f1ebd282e.json | 32 ----- ...c4393a9a8fde2833cf096af766a979d94dee6.json | 18 --- ...9bd10d5717b2a0ddecf22330b7d531aac7c5d.json | 14 -- ...090d45cfa4c85168a587b1a41dc5393cccea1.json | 12 -- ...429d4c9b2cf534e76f35c80a2bf60e8599cca.json | 20 --- ...21ea72db8cfb362e7181c8226d9297507c62b.json | 19 --- ...4351ba0cae3825988b3e0ecd3b6781bcff524.json | 15 --- ...9e34aeaef1154dcd3d6811ab504915497ccf7.json | 14 -- ...f3ff3f9aed7f90027a9ba97634bcb47d772f0.json | 20 --- ...799bd4f82e761837b526a0972c3d4439a264d.json | 16 --- ...b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json | 40 ------ ...139c7b7569602adb58f2d6b3a94da4f167b0a.json | 14 -- ...4ac6a5f34dd820c4073b7728c7067aab9fded.json | 25 ---- ...67b9deb46da75788c7fe6082b43821c22d556.json | 16 --- ...cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json | 20 --- ...ad285680ce157aae9d7837ac020c8b2945e7f.json | 62 --------- core/startos/Cargo.toml | 5 - core/startos/src/analytics/context.rs | 121 ------------------ core/startos/src/analytics/mod.rs | 78 ----------- core/startos/src/bins/analyticsd.rs | 86 ------------- core/startos/src/bins/mod.rs | 4 - core/startos/src/context/config.rs | 3 - core/startos/src/context/rpc.rs | 2 - core/startos/src/lib.rs | 1 - core/startos/src/os_install/mod.rs | 4 - .../db => metrics-db}/registry-sqlx-data.sh | 10 +- .../db => metrics-db}/registry_schema.sql | 0 patch-db | 2 +- sdk/lib/osBindings/GetVersionParams.ts | 7 +- 48 files changed, 11 insertions(+), 1211 deletions(-) delete mode 100755 core/build-analyticsd.sh delete mode 100644 core/startos/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json delete mode 100644 core/startos/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json delete mode 100644 core/startos/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json delete mode 100644 core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json delete mode 100644 core/startos/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json delete mode 100644 core/startos/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json delete mode 100644 core/startos/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json delete mode 100644 core/startos/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json delete mode 100644 core/startos/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json delete mode 100644 core/startos/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json delete mode 100644 core/startos/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json delete mode 100644 core/startos/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json delete mode 100644 core/startos/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json delete mode 100644 core/startos/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json delete mode 100644 core/startos/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json delete mode 100644 core/startos/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json delete mode 100644 core/startos/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json delete mode 100644 core/startos/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json delete mode 100644 core/startos/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json delete mode 100644 core/startos/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json delete mode 100644 core/startos/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json delete mode 100644 core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json delete mode 100644 core/startos/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json delete mode 100644 core/startos/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json delete mode 100644 core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json delete mode 100644 core/startos/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json delete mode 100644 core/startos/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json delete mode 100644 core/startos/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json delete mode 100644 core/startos/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json delete mode 100644 core/startos/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json delete mode 100644 core/startos/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json delete mode 100644 core/startos/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json delete mode 100644 core/startos/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json delete mode 100644 core/startos/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json delete mode 100644 core/startos/src/analytics/context.rs delete mode 100644 core/startos/src/analytics/mod.rs delete mode 100644 core/startos/src/bins/analyticsd.rs rename core/startos/src/registry/{os/version/db => metrics-db}/registry-sqlx-data.sh (65%) rename core/startos/src/registry/{os/version/db => metrics-db}/registry_schema.sql (100%) diff --git a/core/build-analyticsd.sh b/core/build-analyticsd.sh deleted file mode 100755 index 4f8578da1..000000000 --- a/core/build-analyticsd.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -cd "$(dirname "${BASH_SOURCE[0]}")" - -set -e -shopt -s expand_aliases - -if [ -z "$ARCH" ]; then - ARCH=$(uname -m) -fi - -USE_TTY= -if tty -s; then - USE_TTY="-it" -fi - -cd .. -FEATURES="$(echo $ENVIRONMENT | sed 's/-/,/g')" -RUSTFLAGS="" - -if [[ "${ENVIRONMENT}" =~ (^|-)unstable($|-) ]]; then - RUSTFLAGS="--cfg tokio_unstable" -fi - -alias 'rust-musl-builder'='docker run $USE_TTY --rm -e "RUSTFLAGS=$RUSTFLAGS" -v "$HOME/.cargo/registry":/root/.cargo/registry -v "$HOME/.cargo/git":/root/.cargo/git -v "$(pwd)":/home/rust/src -w /home/rust/src -P messense/rust-musl-cross:$ARCH-musl' - -set +e -fail= -echo "FEATURES=\"$FEATURES\"" -echo "RUSTFLAGS=\"$RUSTFLAGS\"" -if ! rust-musl-builder sh -c "(cd core && cargo build --release --no-default-features --features analyticsd,$FEATURES --locked --bin analyticsd --target=$ARCH-unknown-linux-musl)"; then - fail=true -fi -set -e -cd core - -sudo chown -R $USER target -sudo chown -R $USER ~/.cargo - -if [ -n "$fail" ]; then - exit 1 -fi diff --git a/core/startos/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json b/core/startos/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json deleted file mode 100644 index d36100fef..000000000 --- a/core/startos/.sqlx/query-1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Bytea" - ] - }, - "nullable": [] - }, - "hash": "1ce5254f27de971fd87f5ab66d300f2b22433c86617a0dbf796bf2170186dd2e" -} diff --git a/core/startos/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json b/core/startos/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json deleted file mode 100644 index e0b1d7cf2..000000000 --- a/core/startos/.sqlx/query-21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM ssh_keys WHERE fingerprint = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "21471490cdc3adb206274cc68e1ea745ffa5da4479478c1fd2158a45324b1930" -} diff --git a/core/startos/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json b/core/startos/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json deleted file mode 100644 index e234a72a9..000000000 --- a/core/startos/.sqlx/query-28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT hostname, path, username, password FROM cifs_shares WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "hostname", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "username", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "password", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [ - false, - false, - false, - true - ] - }, - "hash": "28ea34bbde836e0618c5fc9bb7c36e463c20c841a7d6a0eb15be0f24f4a928ec" -} diff --git a/core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json b/core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json deleted file mode 100644 index c451ce9f3..000000000 --- a/core/startos/.sqlx/query-350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM tor WHERE package = $1 AND interface = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "350ab82048fb4a049042e4fdbe1b8c606ca400e43e31b9a05d2937217e0f6962" -} diff --git a/core/startos/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json b/core/startos/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json deleted file mode 100644 index 761af064b..000000000 --- a/core/startos/.sqlx/query-4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM ssh_keys WHERE fingerprint = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "fingerprint", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "openssh_pubkey", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "created_at", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false - ] - }, - "hash": "4099028a5c0de578255bf54a67cef6cb0f1e9a4e158260700f1639dd4b438997" -} diff --git a/core/startos/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json b/core/startos/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json deleted file mode 100644 index 1f7edd1ce..000000000 --- a/core/startos/.sqlx/query-4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM session WHERE logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "logged_in", - "type_info": "Timestamp" - }, - { - "ordinal": 2, - "name": "logged_out", - "type_info": "Timestamp" - }, - { - "ordinal": 3, - "name": "last_active", - "type_info": "Timestamp" - }, - { - "ordinal": 4, - "name": "user_agent", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "metadata", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - true, - false, - true, - false - ] - }, - "hash": "4691e3a2ce80b59009ac17124f54f925f61dc5ea371903e62cdffa5d7b67ca96" -} diff --git a/core/startos/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json b/core/startos/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json deleted file mode 100644 index 2157198e5..000000000 --- a/core/startos/.sqlx/query-4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE session SET logged_out = CURRENT_TIMESTAMP WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "4bcfbefb1eb3181343871a1cd7fc3afb81c2be5c681cfa8b4be0ce70610e9c3a" -} diff --git a/core/startos/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json b/core/startos/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json deleted file mode 100644 index 764cff84a..000000000 --- a/core/startos/.sqlx/query-629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT password FROM account", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "password", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "629be61c3c341c131ddbbff0293a83dbc6afd07cae69d246987f62cf0cc35c2a" -} diff --git a/core/startos/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json b/core/startos/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json deleted file mode 100644 index 2e8a9ee0e..000000000 --- a/core/startos/.sqlx/query-687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT key FROM tor WHERE package = $1 AND interface = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "key", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "687688055e63d27123cdc89a5bbbd8361776290a9411d527eaf1fdb40bef399d" -} diff --git a/core/startos/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json b/core/startos/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json deleted file mode 100644 index 3f859bd10..000000000 --- a/core/startos/.sqlx/query-6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE session SET last_active = CURRENT_TIMESTAMP WHERE id = $1 AND logged_out IS NULL OR logged_out > CURRENT_TIMESTAMP", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "6d35ccf780fb2bb62586dd1d3df9c1550a41ee580dad3f49d35cb843ebef10ca" -} diff --git a/core/startos/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json b/core/startos/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json deleted file mode 100644 index cf3591e01..000000000 --- a/core/startos/.sqlx/query-770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO network_keys (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO UPDATE SET package = EXCLUDED.package RETURNING key", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "key", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Bytea" - ] - }, - "nullable": [ - false - ] - }, - "hash": "770c1017734720453dc87b58c385b987c5af5807151ff71a59000014586752e0" -} diff --git a/core/startos/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json b/core/startos/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json deleted file mode 100644 index 53fc6f066..000000000 --- a/core/startos/.sqlx/query-7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications WHERE id < $1 ORDER BY id DESC LIMIT $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "package_id", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "created_at", - "type_info": "Timestamp" - }, - { - "ordinal": 3, - "name": "code", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "level", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "message", - "type_info": "Text" - }, - { - "ordinal": 7, - "name": "data", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int4", - "Int8" - ] - }, - "nullable": [ - false, - true, - false, - false, - false, - false, - false, - true - ] - }, - "hash": "7b64f032d507e8ffe37c41f4c7ad514a66c421a11ab04c26d89a7aa8f6b67210" -} diff --git a/core/startos/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json b/core/startos/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json deleted file mode 100644 index 245a838d8..000000000 --- a/core/startos/.sqlx/query-7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO account (\n id,\n server_id,\n hostname,\n password,\n network_key,\n root_ca_key_pem,\n root_ca_cert_pem\n ) VALUES (\n 0, $1, $2, $3, $4, $5, $6\n ) ON CONFLICT (id) DO UPDATE SET\n server_id = EXCLUDED.server_id,\n hostname = EXCLUDED.hostname,\n password = EXCLUDED.password,\n network_key = EXCLUDED.network_key,\n root_ca_key_pem = EXCLUDED.root_ca_key_pem,\n root_ca_cert_pem = EXCLUDED.root_ca_cert_pem\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Bytea", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "7c7a3549c997eb75bf964ea65fbb98a73045adf618696cd838d79203ef5383fb" -} diff --git a/core/startos/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json b/core/startos/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json deleted file mode 100644 index e3ce7957d..000000000 --- a/core/startos/.sqlx/query-7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM tor WHERE package = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "7e0649d839927e57fa03ee51a2c9f96a8bdb0fc97ee8a3c6df1069e1e2b98576" -} diff --git a/core/startos/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json b/core/startos/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json deleted file mode 100644 index e39aebf69..000000000 --- a/core/startos/.sqlx/query-8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO tor (package, interface, key) VALUES ($1, $2, $3) ON CONFLICT (package, interface) DO NOTHING", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Bytea" - ] - }, - "nullable": [] - }, - "hash": "8951b9126fbf60dbb5997241e11e3526b70bccf3e407327917294a993bc17ed5" -} diff --git a/core/startos/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json b/core/startos/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json deleted file mode 100644 index e7fe8d38c..000000000 --- a/core/startos/.sqlx/query-94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, package_id, created_at, code, level, title, message, data FROM notifications ORDER BY id DESC LIMIT $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "package_id", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "created_at", - "type_info": "Timestamp" - }, - { - "ordinal": 3, - "name": "code", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "level", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "message", - "type_info": "Text" - }, - { - "ordinal": 7, - "name": "data", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - false, - true, - false, - false, - false, - false, - false, - true - ] - }, - "hash": "94d471bb374b4965c6cbedf8c17bbf6bea226d38efaf6559923c79a36d5ca08c" -} diff --git a/core/startos/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json b/core/startos/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json deleted file mode 100644 index aadc0fc3a..000000000 --- a/core/startos/.sqlx/query-95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, hostname, path, username, password FROM cifs_shares", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "hostname", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "path", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "username", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "password", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - true - ] - }, - "hash": "95c4ab4c645f3302568c6ff13d85ab58252362694cf0f56999bf60194d20583a" -} diff --git a/core/startos/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json b/core/startos/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json deleted file mode 100644 index c56a9ebd1..000000000 --- a/core/startos/.sqlx/query-a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM cifs_shares WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [] - }, - "hash": "a60d6e66719325b08dc4ecfacaf337527233c84eee758ac9be967906e5841d27" -} diff --git a/core/startos/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json b/core/startos/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json deleted file mode 100644 index 86bd9250e..000000000 --- a/core/startos/.sqlx/query-a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT fingerprint, openssh_pubkey, created_at FROM ssh_keys", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "fingerprint", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "openssh_pubkey", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "created_at", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false - ] - }, - "hash": "a6b0c8909a3a5d6d9156aebfb359424e6b5a1d1402e028219e21726f1ebd282e" -} diff --git a/core/startos/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json b/core/startos/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json deleted file mode 100644 index c8ff84277..000000000 --- a/core/startos/.sqlx/query-b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE cifs_shares SET hostname = $1, path = $2, username = $3, password = $4 WHERE id = $5", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Text", - "Int4" - ] - }, - "nullable": [] - }, - "hash": "b1147beaaabbed89f2ab8c1e13ec4393a9a8fde2833cf096af766a979d94dee6" -} diff --git a/core/startos/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json b/core/startos/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json deleted file mode 100644 index b76542db8..000000000 --- a/core/startos/.sqlx/query-b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM network_keys WHERE package = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "b203820ee1c553a4b246eac74b79bd10d5717b2a0ddecf22330b7d531aac7c5d" -} diff --git a/core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json b/core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json deleted file mode 100644 index e2e8a1620..000000000 --- a/core/startos/.sqlx/query-b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE account SET tor_key = NULL, network_key = gen_random_bytes(32)", - "describe": { - "columns": [], - "parameters": { - "Left": [] - }, - "nullable": [] - }, - "hash": "b81592b3a74940ab56d41537484090d45cfa4c85168a587b1a41dc5393cccea1" -} diff --git a/core/startos/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json b/core/startos/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json deleted file mode 100644 index b77ba7ce9..000000000 --- a/core/startos/.sqlx/query-d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT openssh_pubkey FROM ssh_keys", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "openssh_pubkey", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "d5117054072476377f3c4f040ea429d4c9b2cf534e76f35c80a2bf60e8599cca" -} diff --git a/core/startos/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json b/core/startos/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json deleted file mode 100644 index 5c5c89c27..000000000 --- a/core/startos/.sqlx/query-da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO notifications (package_id, code, level, title, message, data) VALUES ($1, $2, $3, $4, $5, $6)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Int4", - "Text", - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "da71f94b29798d1738d2b10b9a721ea72db8cfb362e7181c8226d9297507c62b" -} diff --git a/core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json b/core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json deleted file mode 100644 index 2fc8ad1ba..000000000 --- a/core/startos/.sqlx/query-dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM network_keys WHERE package = $1 AND interface = $2", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "dfc23b7e966c3853284753a7e934351ba0cae3825988b3e0ecd3b6781bcff524" -} diff --git a/core/startos/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json b/core/startos/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json deleted file mode 100644 index a4dc187cd..000000000 --- a/core/startos/.sqlx/query-e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM notifications WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [] - }, - "hash": "e185203cf84e43b801dfb23b4159e34aeaef1154dcd3d6811ab504915497ccf7" -} diff --git a/core/startos/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json b/core/startos/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json deleted file mode 100644 index 97a4ec95a..000000000 --- a/core/startos/.sqlx/query-e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT tor_key FROM account WHERE id = 0", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "tor_key", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - true - ] - }, - "hash": "e545696735f202f9d13cf22a561f3ff3f9aed7f90027a9ba97634bcb47d772f0" -} diff --git a/core/startos/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json b/core/startos/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json deleted file mode 100644 index b2aa04370..000000000 --- a/core/startos/.sqlx/query-e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO session (id, user_agent, metadata) VALUES ($1, $2, $3)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "e5843c5b0e7819b29aa1abf2266799bd4f82e761837b526a0972c3d4439a264d" -} diff --git a/core/startos/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json b/core/startos/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json deleted file mode 100644 index fd5a467ec..000000000 --- a/core/startos/.sqlx/query-e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n network_keys.package,\n network_keys.interface,\n network_keys.key,\n tor.key AS \"tor_key?\"\n FROM\n network_keys\n LEFT JOIN\n tor\n ON\n network_keys.package = tor.package\n AND\n network_keys.interface = tor.interface\n WHERE\n network_keys.package = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "package", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "interface", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "key", - "type_info": "Bytea" - }, - { - "ordinal": 3, - "name": "tor_key?", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false - ] - }, - "hash": "e95322a8e2ae3b93f1e974b24c0b81803f1e9ec9e8ebbf15cafddfc1c5a028ed" -} diff --git a/core/startos/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json b/core/startos/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json deleted file mode 100644 index fb8a7c1e5..000000000 --- a/core/startos/.sqlx/query-eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM notifications WHERE id < $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [] - }, - "hash": "eb750adaa305bdbf3c5b70aaf59139c7b7569602adb58f2d6b3a94da4f167b0a" -} diff --git a/core/startos/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json b/core/startos/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json deleted file mode 100644 index 27c9752b2..000000000 --- a/core/startos/.sqlx/query-ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO cifs_shares (hostname, path, username, password) VALUES ($1, $2, $3, $4) RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "ecc765d8205c0876956f95f76944ac6a5f34dd820c4073b7728c7067aab9fded" -} diff --git a/core/startos/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json b/core/startos/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json deleted file mode 100644 index 6ed9898f6..000000000 --- a/core/startos/.sqlx/query-f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO ssh_keys (fingerprint, openssh_pubkey, created_at) VALUES ($1, $2, $3)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "f6d1c5ef0f9d9577bea8382318967b9deb46da75788c7fe6082b43821c22d556" -} diff --git a/core/startos/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json b/core/startos/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json deleted file mode 100644 index f48ccb074..000000000 --- a/core/startos/.sqlx/query-f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT network_key FROM account WHERE id = 0", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "network_key", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - }, - "hash": "f7d2dae84613bcef330f7403352cc96547f3f6dbec11bf2eadfaf53ad8ab51b5" -} diff --git a/core/startos/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json b/core/startos/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json deleted file mode 100644 index 6ef1d5023..000000000 --- a/core/startos/.sqlx/query-fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM account WHERE id = 0", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "password", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "tor_key", - "type_info": "Bytea" - }, - { - "ordinal": 3, - "name": "server_id", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "hostname", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "network_key", - "type_info": "Bytea" - }, - { - "ordinal": 6, - "name": "root_ca_key_pem", - "type_info": "Text" - }, - { - "ordinal": 7, - "name": "root_ca_cert_pem", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - true, - true, - true, - false, - false, - false - ] - }, - "hash": "fe6e4f09f3028e5b6b6259e86cbad285680ce157aae9d7837ac020c8b2945e7f" -} diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index fa2bac69d..539f3de11 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -37,16 +37,11 @@ path = "src/main.rs" name = "registrybox" path = "src/main.rs" -[[bin]] -name = "analyticsd" -path = "src/main.rs" - [features] cli = [] container-runtime = [] daemon = [] registry = [] -analyticsd = [] default = ["cli", "daemon"] dev = [] unstable = ["console-subscriber", "tokio/tracing"] diff --git a/core/startos/src/analytics/context.rs b/core/startos/src/analytics/context.rs deleted file mode 100644 index 8ece8479b..000000000 --- a/core/startos/src/analytics/context.rs +++ /dev/null @@ -1,121 +0,0 @@ -use std::net::{Ipv4Addr, SocketAddr}; -use std::ops::Deref; -use std::path::PathBuf; -use std::sync::Arc; - -use clap::Parser; -use reqwest::{Client, Proxy}; -use rpc_toolkit::yajrc::RpcError; -use rpc_toolkit::{call_remote_http, CallRemote, Context, Empty}; -use serde::{Deserialize, Serialize}; -use sqlx::PgPool; -use tokio::sync::broadcast::Sender; -use tracing::instrument; -use url::Url; - -use crate::context::config::{ContextConfig, CONFIG_PATH}; -use crate::context::RpcContext; -use crate::prelude::*; -use crate::rpc_continuations::RpcContinuations; - -#[derive(Debug, Clone, Default, Deserialize, Serialize, Parser)] -#[serde(rename_all = "kebab-case")] -#[command(rename_all = "kebab-case")] -pub struct AnalyticsConfig { - #[arg(short = 'c', long = "config")] - pub config: Option, - #[arg(short = 'l', long = "listen")] - pub listen: Option, - #[arg(short = 'p', long = "proxy")] - pub tor_proxy: Option, - #[arg(short = 'd', long = "dbconnect")] - pub dbconnect: Option, -} -impl ContextConfig for AnalyticsConfig { - fn next(&mut self) -> Option { - self.config.take() - } - fn merge_with(&mut self, other: Self) { - self.listen = self.listen.take().or(other.listen); - self.tor_proxy = self.tor_proxy.take().or(other.tor_proxy); - self.dbconnect = self.dbconnect.take().or(other.dbconnect); - } -} - -impl AnalyticsConfig { - pub fn load(mut self) -> Result { - let path = self.next(); - self.load_path_rec(path)?; - self.load_path_rec(Some(CONFIG_PATH))?; - Ok(self) - } -} - -pub struct AnalyticsContextSeed { - pub listen: SocketAddr, - pub db: PgPool, - pub rpc_continuations: RpcContinuations, - pub client: Client, - pub shutdown: Sender<()>, -} - -#[derive(Clone)] -pub struct AnalyticsContext(Arc); -impl AnalyticsContext { - #[instrument(skip_all)] - pub async fn init(config: &AnalyticsConfig) -> Result { - let (shutdown, _) = tokio::sync::broadcast::channel(1); - let dbconnect = config - .dbconnect - .clone() - .unwrap_or_else(|| "postgres://localhost/analytics".parse().unwrap()) - .to_owned(); - let db = PgPool::connect(dbconnect.as_str()).await?; - let tor_proxy_url = config - .tor_proxy - .clone() - .map(Ok) - .unwrap_or_else(|| "socks5h://localhost:9050".parse())?; - Ok(Self(Arc::new(AnalyticsContextSeed { - listen: config - .listen - .unwrap_or(SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 5959)), - db, - rpc_continuations: RpcContinuations::new(), - client: Client::builder() - .proxy(Proxy::custom(move |url| { - if url.host_str().map_or(false, |h| h.ends_with(".onion")) { - Some(tor_proxy_url.clone()) - } else { - None - } - })) - .build() - .with_kind(crate::ErrorKind::ParseUrl)?, - shutdown, - }))) - } -} -impl AsRef for AnalyticsContext { - fn as_ref(&self) -> &RpcContinuations { - &self.rpc_continuations - } -} - -impl Context for AnalyticsContext {} -impl Deref for AnalyticsContext { - type Target = AnalyticsContextSeed; - fn deref(&self) -> &Self::Target { - &*self.0 - } -} - -impl CallRemote for RpcContext { - async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result { - if let Some(analytics_url) = self.analytics_url.clone() { - call_remote_http(&self.client, analytics_url, method, params).await - } else { - Ok(Value::Null) - } - } -} diff --git a/core/startos/src/analytics/mod.rs b/core/startos/src/analytics/mod.rs deleted file mode 100644 index 7c84ff386..000000000 --- a/core/startos/src/analytics/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::net::SocketAddr; - -use axum::Router; -use futures::future::ready; -use rpc_toolkit::{Context, ParentHandler, Server}; - -use crate::analytics::context::AnalyticsContext; -use crate::middleware::cors::Cors; -use crate::net::static_server::{bad_request, not_found, server_error}; -use crate::net::web_server::WebServer; -use crate::rpc_continuations::Guid; - -pub mod context; - -pub fn analytics_api() -> ParentHandler { - ParentHandler::new() -} - -pub fn analytics_server_router(ctx: AnalyticsContext) -> Router { - use axum::extract as x; - use axum::routing::{any, get, post}; - Router::new() - .route("/rpc/*path", { - let ctx = ctx.clone(); - post( - Server::new(move || ready(Ok(ctx.clone())), analytics_api()) - .middleware(Cors::new()) - ) - }) - .route( - "/ws/rpc/*path", - get({ - let ctx = ctx.clone(); - move |x::Path(path): x::Path, - ws: axum::extract::ws::WebSocketUpgrade| async move { - match Guid::from(&path) { - None => { - tracing::debug!("No Guid Path"); - bad_request() - } - Some(guid) => match ctx.rpc_continuations.get_ws_handler(&guid).await { - Some(cont) => ws.on_upgrade(cont), - _ => not_found(), - }, - } - } - }), - ) - .route( - "/rest/rpc/*path", - any({ - let ctx = ctx.clone(); - move |request: x::Request| async move { - let path = request - .uri() - .path() - .strip_prefix("/rest/rpc/") - .unwrap_or_default(); - match Guid::from(&path) { - None => { - tracing::debug!("No Guid Path"); - bad_request() - } - Some(guid) => match ctx.rpc_continuations.get_rest_handler(&guid).await { - None => not_found(), - Some(cont) => cont(request).await.unwrap_or_else(server_error), - }, - } - } - }), - ) -} - -impl WebServer { - pub fn analytics(bind: SocketAddr, ctx: AnalyticsContext) -> Self { - Self::new(bind, analytics_server_router(ctx)) - } -} diff --git a/core/startos/src/bins/analyticsd.rs b/core/startos/src/bins/analyticsd.rs deleted file mode 100644 index e37a7dae1..000000000 --- a/core/startos/src/bins/analyticsd.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::ffi::OsString; - -use clap::Parser; -use futures::FutureExt; -use tokio::signal::unix::signal; -use tracing::instrument; - -use crate::analytics::context::{AnalyticsConfig, AnalyticsContext}; -use crate::net::web_server::WebServer; -use crate::prelude::*; -use crate::util::logger::EmbassyLogger; - -#[instrument(skip_all)] -async fn inner_main(config: &AnalyticsConfig) -> Result<(), Error> { - let server = async { - let ctx = AnalyticsContext::init(config).await?; - let server = WebServer::analytics(ctx.listen, ctx.clone()); - - let mut shutdown_recv = ctx.shutdown.subscribe(); - - let sig_handler_ctx = ctx; - let sig_handler = tokio::spawn(async move { - use tokio::signal::unix::SignalKind; - futures::future::select_all( - [ - SignalKind::interrupt(), - SignalKind::quit(), - SignalKind::terminate(), - ] - .iter() - .map(|s| { - async move { - signal(*s) - .unwrap_or_else(|_| panic!("register {:?} handler", s)) - .recv() - .await - } - .boxed() - }), - ) - .await; - sig_handler_ctx - .shutdown - .send(()) - .map_err(|_| ()) - .expect("send shutdown signal"); - }); - - shutdown_recv - .recv() - .await - .with_kind(crate::ErrorKind::Unknown)?; - - sig_handler.abort(); - - Ok::<_, Error>(server) - } - .await?; - server.shutdown().await; - - Ok(()) -} - -pub fn main(args: impl IntoIterator) { - EmbassyLogger::init(); - - let config = AnalyticsConfig::parse_from(args).load().unwrap(); - - let res = { - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .expect("failed to initialize runtime"); - rt.block_on(inner_main(&config)) - }; - - match res { - Ok(()) => (), - Err(e) => { - eprintln!("{}", e.source); - tracing::debug!("{:?}", e.source); - drop(e.source); - std::process::exit(e.kind as i32) - } - } -} diff --git a/core/startos/src/bins/mod.rs b/core/startos/src/bins/mod.rs index 70f87aa99..4a4670a5b 100644 --- a/core/startos/src/bins/mod.rs +++ b/core/startos/src/bins/mod.rs @@ -2,8 +2,6 @@ use std::collections::VecDeque; use std::ffi::OsString; use std::path::Path; -#[cfg(feature = "analytics")] -pub mod analytics; #[cfg(feature = "container-runtime")] pub mod container_cli; pub mod deprecated; @@ -26,8 +24,6 @@ fn select_executable(name: &str) -> Option)> { "startd" => Some(startd::main), #[cfg(feature = "registry")] "registry" => Some(registry::main), - #[cfg(feature = "analyticsd")] - "analyticsd" => Some(analyticsd::main), "embassy-cli" => Some(|_| deprecated::renamed("embassy-cli", "start-cli")), "embassy-sdk" => Some(|_| deprecated::renamed("embassy-sdk", "start-sdk")), "embassyd" => Some(|_| deprecated::renamed("embassyd", "startd")), diff --git a/core/startos/src/context/config.rs b/core/startos/src/context/config.rs index 886361777..bc2da00e2 100644 --- a/core/startos/src/context/config.rs +++ b/core/startos/src/context/config.rs @@ -113,8 +113,6 @@ pub struct ServerConfig { pub datadir: Option, #[arg(long = "disable-encryption")] pub disable_encryption: Option, - #[arg(short = 'a', long = "analytics-url")] - pub analytics_url: Option, } impl ContextConfig for ServerConfig { fn next(&mut self) -> Option { @@ -133,7 +131,6 @@ impl ContextConfig for ServerConfig { .or(other.revision_cache_size); self.datadir = self.datadir.take().or(other.datadir); self.disable_encryption = self.disable_encryption.take().or(other.disable_encryption); - self.analytics_url = self.analytics_url.take().or(other.analytics_url); } } diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index 1042def74..cac1b46e0 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -58,7 +58,6 @@ pub struct RpcContextSeed { pub start_time: Instant, #[cfg(feature = "dev")] pub dev: Dev, - pub analytics_url: Option, } pub struct Dev { @@ -191,7 +190,6 @@ impl RpcContext { dev: Dev { lxc: Mutex::new(BTreeMap::new()), }, - analytics_url: config.analytics_url.clone(), }); let res = Self(seed.clone()); diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index 96210114f..228ac27c8 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -24,7 +24,6 @@ lazy_static::lazy_static! { pub mod account; pub mod action; -pub mod analytics; pub mod auth; pub mod backup; pub mod bins; diff --git a/core/startos/src/os_install/mod.rs b/core/startos/src/os_install/mod.rs index b1d9ef1c4..c3931b236 100644 --- a/core/startos/src/os_install/mod.rs +++ b/core/startos/src/os_install/mod.rs @@ -281,10 +281,6 @@ pub async fn execute( IoFormat::Yaml.to_vec(&ServerConfig { os_partitions: Some(part_info.clone()), ethernet_interface: Some(eth_iface), - #[cfg(feature = "dev")] - analytics_url: None, - #[cfg(not(feature = "dev"))] - analytics_url: Some("https://analytics.start9.com".parse()?), // TODO: FullMetal ..Default::default() })?, ) diff --git a/core/startos/src/registry/os/version/db/registry-sqlx-data.sh b/core/startos/src/registry/metrics-db/registry-sqlx-data.sh similarity index 65% rename from core/startos/src/registry/os/version/db/registry-sqlx-data.sh rename to core/startos/src/registry/metrics-db/registry-sqlx-data.sh index 2a422bf38..2b24873a4 100755 --- a/core/startos/src/registry/os/version/db/registry-sqlx-data.sh +++ b/core/startos/src/registry/metrics-db/registry-sqlx-data.sh @@ -1,6 +1,6 @@ #!/bin/bash -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +cd "$(dirname "${BASH_SOURCE[0]}")" TMP_DIR=$(mktemp -d) mkdir $TMP_DIR/pgdata docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR/pgdata:/var/lib/postgresql/data postgres @@ -8,17 +8,15 @@ docker run -d --rm --name=tmp_postgres -e POSTGRES_PASSWORD=password -v $TMP_DIR ( set -e ctr=0 - until docker exec tmp_postgres psql -U postgres || [ $ctr -ge 5 ]; do + until docker exec tmp_postgres psql -U postgres 2> /dev/null || [ $ctr -ge 5 ]; do ctr=$[ctr + 1] sleep 5; done PG_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' tmp_postgres) - - SCHEMA_DUMP="registry_schema.sql" - DATABASE_URL=postgres://postgres:password@$PG_IP/postgres - psql $DATABASE_URL -f "$SCRIPT_DIR/$SCHEMA_DUMP" + cat "./registry_schema.sql" | docker exec -i tmp_postgres psql -U postgres -d postgres -f- + cd ../../.. DATABASE_URL=postgres://postgres:password@$PG_IP/postgres PLATFORM=$(uname -m) cargo sqlx prepare -- --lib --profile=test --workspace echo "Subscript Complete" ) diff --git a/core/startos/src/registry/os/version/db/registry_schema.sql b/core/startos/src/registry/metrics-db/registry_schema.sql similarity index 100% rename from core/startos/src/registry/os/version/db/registry_schema.sql rename to core/startos/src/registry/metrics-db/registry_schema.sql diff --git a/patch-db b/patch-db index 88a804f56..99076d349 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 88a804f56f446d34896ef331d915b821a581cf01 +Subproject commit 99076d349c6768000483ea8d47216d273586552e diff --git a/sdk/lib/osBindings/GetVersionParams.ts b/sdk/lib/osBindings/GetVersionParams.ts index 71762c8dd..853d76022 100644 --- a/sdk/lib/osBindings/GetVersionParams.ts +++ b/sdk/lib/osBindings/GetVersionParams.ts @@ -1,3 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type GetVersionParams = { source: string | null; target: string | null } +export type GetVersionParams = { + source: string | null + target: string | null + serverId: string | null + arch: string | null +} From 9da49be44d0da70c28755dd531ea689813fc10c6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:15:56 -0600 Subject: [PATCH 14/20] Bugfix/patch db subscriber (#2652) * fix socket sending empty patches * do not timeout tcp connections, just poll them more * switch from poll to tcp keepalive --- core/Cargo.lock | 2 + core/startos/Cargo.toml | 2 + core/startos/src/net/utils.rs | 18 ------ core/startos/src/net/vhost.rs | 85 ++++++++++++++++------------ core/startos/src/s9pk/v2/manifest.rs | 2 +- core/startos/src/util/io.rs | 36 +++++++++--- patch-db | 2 +- 7 files changed, 83 insertions(+), 64 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index 031ec41ab..f8db0bd7e 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -4881,6 +4881,7 @@ dependencies = [ "hmac", "http 1.1.0", "http-body-util", + "hyper-util", "id-pool", "imbl", "imbl-value", @@ -4936,6 +4937,7 @@ dependencies = [ "sha2 0.10.8", "shell-words", "simple-logging", + "socket2", "sqlx", "sscanf", "ssh-key", diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index a8707bf65..750bc4953 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -97,6 +97,7 @@ hex = "0.4.3" hmac = "0.12.1" http = "1.0.0" http-body-util = "0.1" +hyper-util = { version = "0.1.5", features = ["tokio", "service"] } id-pool = { version = "0.2.2", default-features = false, features = [ "serde", "u16", @@ -159,6 +160,7 @@ serde_yaml = { package = "serde_yml", version = "0.0.10" } sha2 = "0.10.2" shell-words = "1" simple-logging = "2.0.2" +socket2 = "0.5.7" sqlx = { version = "0.7.2", features = [ "chrono", "runtime-tokio-rustls", diff --git a/core/startos/src/net/utils.rs b/core/startos/src/net/utils.rs index 6de319a5e..9cba8a0cd 100644 --- a/core/startos/src/net/utils.rs +++ b/core/startos/src/net/utils.rs @@ -112,24 +112,6 @@ pub async fn find_eth_iface() -> Result { )) } -#[pin_project::pin_project] -pub struct SingleAccept(Option); -impl SingleAccept { - pub fn new(conn: T) -> Self { - Self(Some(conn)) - } -} -// impl axum_server::accept::Accept for SingleAccept { -// type Conn = T; -// type Error = Infallible; -// fn poll_accept( -// self: std::pin::Pin<&mut Self>, -// _cx: &mut std::task::Context<'_>, -// ) -> std::task::Poll>> { -// std::task::Poll::Ready(self.project().0.take().map(Ok)) -// } -// } - pub struct TcpListeners { listeners: Vec, } diff --git a/core/startos/src/net/vhost.rs b/core/startos/src/net/vhost.rs index b4f5715ae..e6a9d5b21 100644 --- a/core/startos/src/net/vhost.rs +++ b/core/startos/src/net/vhost.rs @@ -1,10 +1,15 @@ use std::collections::BTreeMap; use std::net::{IpAddr, Ipv6Addr, SocketAddr}; +use std::str::FromStr; use std::sync::{Arc, Weak}; use std::time::Duration; +use axum::body::Body; +use axum::extract::Request; +use axum::response::Response; use color_eyre::eyre::eyre; use helpers::NonDetachingJoinHandle; +use http::Uri; use imbl_value::InternedString; use models::ResultExt; use serde::{Deserialize, Serialize}; @@ -20,8 +25,9 @@ use tracing::instrument; use ts_rs::TS; use crate::db::model::Database; +use crate::net::static_server::server_error; use crate::prelude::*; -use crate::util::io::{BackTrackingReader, TimeoutStream}; +use crate::util::io::BackTrackingReader; use crate::util::serde::MaybeUtf8String; // not allowed: <=1024, >=32768, 5355, 5432, 9050, 6010, 9051, 5353 @@ -113,8 +119,16 @@ impl VHostServer { loop { match listener.accept().await { Ok((stream, _)) => { - let stream = - Box::pin(TimeoutStream::new(stream, Duration::from_secs(300))); + if let Err(e) = socket2::SockRef::from(&stream).set_tcp_keepalive( + &socket2::TcpKeepalive::new() + .with_time(Duration::from_secs(900)) + .with_interval(Duration::from_secs(60)) + .with_retries(5), + ) { + tracing::error!("Failed to set tcp keepalive: {e}"); + tracing::debug!("{e:?}"); + } + let mut stream = BackTrackingReader::new(stream); stream.start_buffering(); let mapping = mapping.clone(); @@ -129,38 +143,39 @@ impl VHostServer { { Ok(a) => a, Err(_) => { - // stream.rewind(); - // return hyper::server::Server::builder( - // SingleAccept::new(stream), - // ) - // .serve(make_service_fn(|_| async { - // Ok::<_, Infallible>(service_fn(|req| async move { - // let host = req - // .headers() - // .get(http::header::HOST) - // .and_then(|host| host.to_str().ok()); - // let uri = Uri::from_parts({ - // let mut parts = - // req.uri().to_owned().into_parts(); - // parts.authority = host - // .map(FromStr::from_str) - // .transpose()?; - // parts - // })?; - // Response::builder() - // .status( - // http::StatusCode::TEMPORARY_REDIRECT, - // ) - // .header( - // http::header::LOCATION, - // uri.to_string(), - // ) - // .body(Body::default()) - // })) - // })) - // .await - // .with_kind(crate::ErrorKind::Network); - todo!() + stream.rewind(); + return hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new()) + .serve_connection( + hyper_util::rt::TokioIo::new(stream), + hyper_util::service::TowerToHyperService::new(axum::Router::new().fallback( + axum::routing::method_routing::any(move |req: Request| async move { + match async move { + let host = req + .headers() + .get(http::header::HOST) + .and_then(|host| host.to_str().ok()); + let uri = Uri::from_parts({ + let mut parts = req.uri().to_owned().into_parts(); + parts.authority = host.map(FromStr::from_str).transpose()?; + parts + })?; + Response::builder() + .status(http::StatusCode::TEMPORARY_REDIRECT) + .header(http::header::LOCATION, uri.to_string()) + .body(Body::default()) + }.await { + Ok(a) => a, + Err(e) => { + tracing::warn!("Error redirecting http request on ssl port: {e}"); + tracing::error!("{e:?}"); + server_error(Error::new(e, ErrorKind::Network)) + } + } + }), + )), + ) + .await + .map_err(|e| Error::new(color_eyre::eyre::Report::msg(e), ErrorKind::Network)); } }; let target_name = diff --git a/core/startos/src/s9pk/v2/manifest.rs b/core/startos/src/s9pk/v2/manifest.rs index 77e48c126..9ae8524fa 100644 --- a/core/startos/src/s9pk/v2/manifest.rs +++ b/core/startos/src/s9pk/v2/manifest.rs @@ -146,7 +146,7 @@ impl Manifest { #[ts(export)] pub struct HardwareRequirements { #[serde(default)] - #[ts(type = "{ [key: string]: string }")] + #[ts(type = "{ [key: string]: string }")] // TODO more specific key pub device: BTreeMap, #[ts(type = "number | null")] pub ram: Option, diff --git a/core/startos/src/util/io.rs b/core/startos/src/util/io.rs index 9a6bab64b..b5748d1d9 100644 --- a/core/startos/src/util/io.rs +++ b/core/startos/src/util/io.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, VecDeque}; +use std::collections::VecDeque; use std::future::Future; use std::io::Cursor; use std::os::unix::prelude::MetadataExt; @@ -706,16 +706,16 @@ impl AsyncRead for TimeoutStream { buf: &mut tokio::io::ReadBuf<'_>, ) -> std::task::Poll> { let mut this = self.project(); - if let std::task::Poll::Ready(_) = this.sleep.as_mut().poll(cx) { + let timeout = this.sleep.as_mut().poll(cx); + let res = this.stream.poll_read(cx, buf); + if res.is_ready() { + this.sleep.reset(Instant::now() + *this.timeout); + } else if timeout.is_ready() { return std::task::Poll::Ready(Err(std::io::Error::new( std::io::ErrorKind::TimedOut, "timed out", ))); } - let res = this.stream.poll_read(cx, buf); - if res.is_ready() { - this.sleep.reset(Instant::now() + *this.timeout); - } res } } @@ -725,10 +725,16 @@ impl AsyncWrite for TimeoutStream { cx: &mut std::task::Context<'_>, buf: &[u8], ) -> std::task::Poll> { - let this = self.project(); + let mut this = self.project(); + let timeout = this.sleep.as_mut().poll(cx); let res = this.stream.poll_write(cx, buf); if res.is_ready() { this.sleep.reset(Instant::now() + *this.timeout); + } else if timeout.is_ready() { + return std::task::Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::TimedOut, + "timed out", + ))); } res } @@ -736,10 +742,16 @@ impl AsyncWrite for TimeoutStream { self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let this = self.project(); + let mut this = self.project(); + let timeout = this.sleep.as_mut().poll(cx); let res = this.stream.poll_flush(cx); if res.is_ready() { this.sleep.reset(Instant::now() + *this.timeout); + } else if timeout.is_ready() { + return std::task::Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::TimedOut, + "timed out", + ))); } res } @@ -747,10 +759,16 @@ impl AsyncWrite for TimeoutStream { self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - let this = self.project(); + let mut this = self.project(); + let timeout = this.sleep.as_mut().poll(cx); let res = this.stream.poll_shutdown(cx); if res.is_ready() { this.sleep.reset(Instant::now() + *this.timeout); + } else if timeout.is_ready() { + return std::task::Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::TimedOut, + "timed out", + ))); } res } diff --git a/patch-db b/patch-db index c537a07ea..99076d349 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit c537a07ea937e69b66841d903c70fd75623e5457 +Subproject commit 99076d349c6768000483ea8d47216d273586552e From 0c188f6d10fe7380a3b6b757906304aeea28944a Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 25 Jun 2024 10:54:09 -0600 Subject: [PATCH 15/20] fix ca trust test and snek high score --- web/package-lock.json | 12 +++++++----- web/projects/ui/src/app/app/snek/snek.directive.ts | 2 +- .../app/pages/login/ca-wizard/ca-wizard.component.ts | 2 +- web/projects/ui/src/app/services/api/api.types.ts | 5 ++++- .../ui/src/app/services/api/embassy-api.service.ts | 4 +++- .../src/app/services/api/embassy-live-api.service.ts | 4 ++++ .../src/app/services/api/embassy-mock-api.service.ts | 12 +++++++++++- 7 files changed, 31 insertions(+), 10 deletions(-) diff --git a/web/package-lock.json b/web/package-lock.json index 81b612ac1..ca5c8efe8 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1974,22 +1974,24 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha1", + "version": "0.3.6-alpha5", "license": "MIT", "dependencies": { + "@iarna/toml": "^2.2.5", "isomorphic-fetch": "^3.0.0", - "ts-matches": "^5.4.1" + "lodash": "^4.17.21", + "ts-matches": "^5.4.1", + "yaml": "^2.2.2" }, "devDependencies": { - "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", + "@types/lodash": "^4.17.5", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "tsx": "^4.7.1", - "typescript": "^5.0.4", - "yaml": "^2.2.2" + "typescript": "^5.0.4" } }, "node_modules/@adobe/css-tools": { diff --git a/web/projects/ui/src/app/app/snek/snek.directive.ts b/web/projects/ui/src/app/app/snek/snek.directive.ts index 255926792..db812ae4a 100644 --- a/web/projects/ui/src/app/app/snek/snek.directive.ts +++ b/web/projects/ui/src/app/app/snek/snek.directive.ts @@ -39,7 +39,7 @@ export class SnekDirective { try { await this.embassyApi.setDbValue( - ['gaming', 'snake', 'high-score'], + ['gaming', 'snake', 'highScore'], data.highScore, ) } catch (e: any) { diff --git a/web/projects/ui/src/app/pages/login/ca-wizard/ca-wizard.component.ts b/web/projects/ui/src/app/pages/login/ca-wizard/ca-wizard.component.ts index 2c0e9c7fe..fde1c968f 100644 --- a/web/projects/ui/src/app/pages/login/ca-wizard/ca-wizard.component.ts +++ b/web/projects/ui/src/app/pages/login/ca-wizard/ca-wizard.component.ts @@ -42,7 +42,7 @@ export class CAWizardComponent { private async testHttps() { const url = `https://${this.document.location.host}${this.relativeUrl}` - await this.api.getState().then(() => { + await this.api.echo({ message: 'ping' }, url).then(() => { this.caTrusted = true }) } diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index a5c52e9a2..0c7679754 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -12,7 +12,10 @@ export module RR { export type WebsocketConfig = Omit, 'url'> - // server state + // state + + export type EchoReq = { message: string } // server.echo + export type EchoRes = string export type ServerState = 'initializing' | 'error' | 'running' diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index d6bb11632..1d9c6f805 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -17,7 +17,9 @@ export abstract class ApiService { config: RR.WebsocketConfig, ): Observable - // server state + // state + + abstract echo(params: RR.EchoReq, url: string): Promise abstract getState(): Promise diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 954a4475e..057c51013 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -71,6 +71,10 @@ export class LiveApiService extends ApiService { // state + async echo(params: RR.EchoReq, url: string): Promise { + return this.rpcRequest({ method: 'echo', params }, url) + } + async getState(): Promise { return this.rpcRequest({ method: 'state', params: {} }) } diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 728b4ff35..98efee2ef 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -118,7 +118,17 @@ export class MockApiService extends ApiService { } } - // server state + // state + + async echo(params: RR.EchoReq, url: string): Promise { + if (url) { + const num = Math.floor(Math.random() * 10) + 1 + if (num > 8) return params.message + throw new Error() + } + await pauseFor(2000) + return params.message + } private stateIndex = 0 async getState(): Promise { From 0e506f571611a3b67b94e6a8482b268f02642a1a Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Tue, 25 Jun 2024 12:34:47 -0600 Subject: [PATCH 16/20] fix container cli (#2654) --- core/Cargo.lock | 490 +++++------------- core/startos/src/context/cli.rs | 27 +- core/startos/src/service/cli.rs | 27 +- .../src/service/service_effect_handler.rs | 39 +- 4 files changed, 173 insertions(+), 410 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index f8db0bd7e..6db13b8cd 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -225,7 +225,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -236,7 +236,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -432,7 +432,7 @@ checksum = "be5951c75bdabb58753d140dd5802f12ff3a483cb2e16fb5276e111b94b19e87" dependencies = [ "concurrent-queue 1.2.4", "event-listener", - "spin 0.9.8", + "spin", ] [[package]] @@ -624,9 +624,9 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" dependencies = [ "jobserver", "libc", @@ -740,7 +740,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -914,6 +914,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] @@ -935,6 +936,23 @@ dependencies = [ "url", ] +[[package]] +name = "cookie_store" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4934e6b7e8419148b6ef56950d277af8561060b56afd59e2aadf98b59fce6baa" +dependencies = [ + "cookie 0.18.1", + "idna 0.5.0", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1126,16 +1144,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -1149,7 +1166,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1173,7 +1190,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1184,7 +1201,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1215,7 +1232,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1238,7 +1255,7 @@ checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1253,15 +1270,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.68", ] [[package]] @@ -1306,17 +1323,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "displaydoc" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "divrem" version = "1.0.0" @@ -1398,7 +1404,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ - "curve25519-dalek 4.1.2", + "curve25519-dalek 4.1.3", "ed25519 2.2.3", "rand_core 0.6.4", "serde", @@ -1487,7 +1493,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -1600,7 +1606,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -1715,7 +1721,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -2055,9 +2061,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2116,6 +2122,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.23.10", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -2187,124 +2210,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "id-pool" version = "0.2.2" @@ -2342,14 +2247,12 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", - "smallvec", - "utf8_iter", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -2389,18 +2292,18 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -2727,11 +2630,11 @@ checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] @@ -2779,12 +2682,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lock_api" version = "0.4.12" @@ -2852,9 +2749,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2897,9 +2794,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -3170,7 +3067,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -3236,7 +3133,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -3335,7 +3232,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -3454,7 +3351,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -3496,12 +3393,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" - [[package]] name = "portable-atomic" version = "1.6.0" @@ -3560,18 +3451,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", @@ -3618,7 +3509,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -3802,9 +3693,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -3866,14 +3757,14 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", "bytes", - "cookie 0.17.0", - "cookie_store", + "cookie 0.18.1", + "cookie_store 0.21.0", "encoding_rs", "futures-core", "futures-util", @@ -3882,6 +3773,7 @@ dependencies = [ "http-body 1.0.0", "http-body-util", "hyper 1.3.1", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -3896,7 +3788,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -3918,7 +3810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93ea5c6f30c19d766efe8d823c88f9abd1c56516648a0d4264ab2dc04cc19472" dependencies = [ "bytes", - "cookie_store", + "cookie_store 0.20.0", "reqwest", "url", ] @@ -3943,7 +3835,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.15", "libc", - "spin 0.9.8", + "spin", "untrusted", "windows-sys 0.52.0", ] @@ -3962,7 +3854,7 @@ dependencies = [ [[package]] name = "rpc-toolkit" version = "0.2.3" -source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor/no-dyn-ctx#5a24903031e72ac75fd23889215361edc7b20842" +source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor/no-dyn-ctx#f608480034942f1f521ab95949ab33fbc51d99a9" dependencies = [ "async-stream", "async-trait", @@ -4088,6 +3980,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls" +version = "0.23.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.102.4", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4288,7 +4193,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -4361,7 +4266,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -4529,12 +4434,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -4788,7 +4687,7 @@ dependencies = [ "quote", "regex-syntax 0.6.29", "strsim 0.10.0", - "syn 2.0.66", + "syn 2.0.68", "unicode-width", ] @@ -4834,12 +4733,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "start-os" version = "0.3.5-rev.2" @@ -4865,7 +4758,7 @@ dependencies = [ "console", "console-subscriber", "cookie 0.18.1", - "cookie_store", + "cookie_store 0.20.0", "der", "digest 0.10.7", "divrem", @@ -4945,7 +4838,7 @@ dependencies = [ "tar", "thiserror", "tokio", - "tokio-rustls", + "tokio-rustls 0.25.0", "tokio-socks", "tokio-stream", "tokio-tar", @@ -5019,9 +4912,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -5036,9 +4929,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ "proc-macro2", "quote", @@ -5057,17 +4950,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", -] - [[package]] name = "system-configuration" version = "0.5.1" @@ -5165,7 +5047,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -5229,16 +5111,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -5292,7 +5164,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -5316,6 +5188,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.10", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-socks" version = "0.5.1" @@ -5549,7 +5432,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -5701,7 +5584,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", "termcolor", ] @@ -5742,7 +5625,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -5825,12 +5708,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 1.0.0", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -5847,18 +5730,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -5867,9 +5738,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.8.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom 0.2.15", ] @@ -5959,7 +5830,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", "wasm-bindgen-shared", ] @@ -5993,7 +5864,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.68", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6250,18 +6121,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "wyz" version = "0.5.1" @@ -6324,30 +6183,6 @@ dependencies = [ "serde", ] -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.7.34" @@ -6365,28 +6200,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", - "synstructure", + "syn 2.0.68", ] [[package]] @@ -6406,29 +6220,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", -] - -[[package]] -name = "zerovec" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.66", + "syn 2.0.68", ] [[package]] @@ -6451,9 +6243,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", diff --git a/core/startos/src/context/cli.rs b/core/startos/src/context/cli.rs index 22084bbe1..d560d575d 100644 --- a/core/startos/src/context/cli.rs +++ b/core/startos/src/context/cli.rs @@ -25,7 +25,7 @@ use crate::rpc_continuations::Guid; #[derive(Debug)] pub struct CliContextSeed { - pub runtime: OnceCell, + pub runtime: OnceCell>, pub base_url: Url, pub rpc_url: Url, pub registry_url: Option, @@ -249,16 +249,19 @@ impl std::ops::Deref for CliContext { } } impl Context for CliContext { - fn runtime(&self) -> tokio::runtime::Handle { - self.runtime - .get_or_init(|| { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - }) - .handle() - .clone() + fn runtime(&self) -> Option> { + Some( + self.runtime + .get_or_init(|| { + Arc::new( + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(), + ) + }) + .clone(), + ) } } impl CallRemote for CliContext { @@ -290,7 +293,7 @@ impl CallRemote for CliContext { #[test] fn test() { let ctx = CliContext::init(ClientConfig::default()).unwrap(); - ctx.runtime().block_on(async { + ctx.runtime().unwrap().block_on(async { reqwest::Client::new() .get("http://example.com") .send() diff --git a/core/startos/src/service/cli.rs b/core/startos/src/service/cli.rs index 87491932b..f5e04999c 100644 --- a/core/startos/src/service/cli.rs +++ b/core/startos/src/service/cli.rs @@ -19,7 +19,7 @@ pub struct ContainerClientConfig { pub struct ContainerCliSeed { socket: PathBuf, - runtime: OnceCell, + runtime: OnceCell>, } #[derive(Clone)] @@ -35,17 +35,20 @@ impl ContainerCliContext { } } impl Context for ContainerCliContext { - fn runtime(&self) -> tokio::runtime::Handle { - self.0 - .runtime - .get_or_init(|| { - tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - }) - .handle() - .clone() + fn runtime(&self) -> Option> { + Some( + self.0 + .runtime + .get_or_init(|| { + Arc::new( + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(), + ) + }) + .clone(), + ) } } diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index a61a00b9b..63c313ead 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -899,50 +899,15 @@ async fn running(context: EffectContext, params: ParamsPackageId) -> Result Result { - Ok(Self { - procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), - }) - } - fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { - Ok(Self { - procedure_id: matches.get_one("procedure-id").cloned().unwrap_or_default(), - }) - } - fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { - self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); - Ok(()) - } - fn update_from_arg_matches_mut( - &mut self, - matches: &mut clap::ArgMatches, - ) -> Result<(), clap::Error> { - self.procedure_id = matches.get_one("procedure-id").cloned().unwrap_or_default(); - Ok(()) - } -} -impl CommandFactory for ProcedureId { - fn command() -> clap::Command { - Self::command_for_update().arg( - clap::Arg::new("procedure-id") - .action(clap::ArgAction::Set) - .value_parser(clap::value_parser!(Guid)), - ) - } - fn command_for_update() -> clap::Command { - Self::command() - } -} - async fn restart( context: EffectContext, ProcedureId { procedure_id }: ProcedureId, From ab1fdf69c8ea4b3ef260c9881f433cc09e32ca6f Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:11:11 -0600 Subject: [PATCH 17/20] add docs for development environment (#2655) --- DEVELOPMENT.md | 133 +++++++++++++++++++++++++++ Makefile | 37 +++++++- assets/create-vm/step-1.png | Bin 0 -> 28070 bytes assets/create-vm/step-10.png | Bin 0 -> 45200 bytes assets/create-vm/step-11.png | Bin 0 -> 54662 bytes assets/create-vm/step-12.png | Bin 0 -> 49039 bytes assets/create-vm/step-2.png | Bin 0 -> 47071 bytes assets/create-vm/step-3.png | Bin 0 -> 46699 bytes assets/create-vm/step-4.png | Bin 0 -> 51398 bytes assets/create-vm/step-5.png | Bin 0 -> 65075 bytes assets/create-vm/step-6.png | Bin 0 -> 53461 bytes assets/create-vm/step-7.png | Bin 0 -> 65527 bytes assets/create-vm/step-8.png | Bin 0 -> 53991 bytes assets/create-vm/step-9.png | Bin 0 -> 45452 bytes build/lib/scripts/chroot-and-upgrade | 17 +--- build/lib/scripts/prune-images | 49 ++++++++++ 16 files changed, 217 insertions(+), 19 deletions(-) create mode 100644 DEVELOPMENT.md create mode 100644 assets/create-vm/step-1.png create mode 100644 assets/create-vm/step-10.png create mode 100644 assets/create-vm/step-11.png create mode 100644 assets/create-vm/step-12.png create mode 100644 assets/create-vm/step-2.png create mode 100644 assets/create-vm/step-3.png create mode 100644 assets/create-vm/step-4.png create mode 100644 assets/create-vm/step-5.png create mode 100644 assets/create-vm/step-6.png create mode 100644 assets/create-vm/step-7.png create mode 100644 assets/create-vm/step-8.png create mode 100644 assets/create-vm/step-9.png create mode 100755 build/lib/scripts/prune-images diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..cb9385f41 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,133 @@ +# Setting up your development environment on Debian/Ubuntu + +A step-by-step guide + +> This is the only officially supported build environment. +> MacOS has limited build capabilities and Windows requires [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) + +## Installing dependencies + +Run the following commands one at a time + +```sh +sudo apt update +sudo apt install -y ca-certificates curl gpg build-essential +curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +echo "deb [arch=$(dpkg-architecture -q DEB_HOST_ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bookworm stable" | sudo tee /etc/apt/sources.list.d/docker.list +sudo apt update +sudo apt install -y sed grep gawk jq gzip brotli containerd.io docker-ce docker-ce-cli docker-compose-plugin qemu-user-static binfmt-support squashfs-tools git debspawn rsync b3sum +sudo mkdir -p /etc/debspawn/ +echo "AllowUnsafePermissions=true" | sudo tee /etc/debspawn/global.toml +sudo usermod -aG docker $USER +sudo su $USER +docker run --privileged --rm tonistiigi/binfmt --install all +docker buildx create --use +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # proceed with default installation +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash +source ~/.bashrc +nvm install 20 +nvm use 20 +``` + +## Cloning the repository + +```sh +git clone --recursive https://github.com/Start9Labs/start-os.git --branch next/minor +cd start-os +``` + +## Building an ISO + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make iso +``` + +This will build an ISO for your current architecture. If you are building to run on an architecture other than the one you are currently on, replace `$(uname -m)` with the correct platform for the device (one of `aarch64`, `aarch64-nonfree`, `x86_64`, `x86_64-nonfree`, `raspberrypi`) + +## Creating a VM + +### Install virt-manager + +```sh +sudo apt update +sudo apt install -y virt-manager +sudo usermod -aG libvirt $USER +sudo su $USER +``` + +### Launch virt-manager + +```sh +virt-manager +``` + +### Create new virtual machine + +![Select "Create a new virtual machine"](assets/create-vm/step-1.png) +![Click "Forward"](assets/create-vm/step-2.png) +![Click "Browse"](assets/create-vm/step-3.png) +![Click "+"](assets/create-vm/step-4.png) + +#### make sure to set "Target Path" to the path to your results directory in start-os + +![Create storage pool](assets/create-vm/step-5.png) +![Select storage pool](assets/create-vm/step-6.png) +![Select ISO](assets/create-vm/step-7.png) +![Select "Generic or unknown OS" and click "Forward"](assets/create-vm/step-8.png) +![Set Memory and CPUs](assets/create-vm/step-9.png) +![Create disk](assets/create-vm/step-10.png) +![Name VM](assets/create-vm/step-11.png) +![Create network](assets/create-vm/step-12.png) + +## Updating a VM + +The fastest way to update a VM to your latest code depends on what you changed: + +### UI or startd: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make update-startbox REMOTE=start9@ +``` + +### Container runtime or debian dependencies: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make update-deb REMOTE=start9@ +``` + +### Image recipe: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make update-squashfs REMOTE=start9@ +``` + +--- + +If the device you are building for is not available via ssh, it is also possible to use `magic-wormhole` to send the relevant files. + +### Prerequisites: + +```sh +sudo apt update +sudo apt install -y magic-wormhole +``` + +As before, the fastest way to update a VM to your latest code depends on what you changed. Each of the following commands will return a command to paste into the shell of the device you would like to upgrade. + +### UI or startd: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make wormhole +``` + +### Container runtime or debian dependencies: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make wormhole-deb +``` + +### Image recipe: + +```sh +PLATFORM=$(uname -m) ENVIRONMENT=dev make wormhole-squashfs +``` diff --git a/Makefile b/Makefile index 5a0440196..5e038a08d 100644 --- a/Makefile +++ b/Makefile @@ -152,16 +152,21 @@ update-overlay: $(ALL_TARGETS) $(call ssh,"sudo systemctl start startd") wormhole: core/target/$(ARCH)-unknown-linux-musl/release/startbox - @echo "Paste the following command into the shell of your start-os server:" + @echo "Paste the following command into the shell of your StartOS server:" + @echo @wormhole send core/target/$(ARCH)-unknown-linux-musl/release/startbox 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade \"cd /usr/bin && rm startbox && wormhole receive --accept-file %s && chmod +x startbox\"\n", $$3 }' wormhole-deb: results/$(BASENAME).deb - @echo "Paste the following command into the shell of your start-os server:" + @echo "Paste the following command into the shell of your StartOS server:" + @echo @wormhole send results/$(BASENAME).deb 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade '"'"'cd $$(mktemp -d) && wormhole receive --accept-file %s && apt-get install -y --reinstall ./$(BASENAME).deb'"'"'\n", $$3 }' -wormhole-cli: core/target/$(ARCH)-unknown-linux-musl/release/start-cli - @echo "Paste the following command into the shell of your start-os server:" - @wormhole send results/$(BASENAME).deb 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo /usr/lib/startos/scripts/chroot-and-upgrade '"'"'cd $$(mktemp -d) && wormhole receive --accept-file %s && apt-get install -y --reinstall ./$(BASENAME).deb'"'"'\n", $$3 }' +wormhole-squashfs: results/$(BASENAME).squashfs + $(eval SQFS_SUM := $(shell b3sum results/$(BASENAME).squashfs | head -c 32)) + $(eval SQFS_SIZE := $(shell du -s --bytes results/$(BASENAME).squashfs | awk '{print $$1}')) + @echo "Paste the following command into the shell of your StartOS server:" + @echo + @wormhole send results/$(BASENAME).squashfs 2>&1 | awk -Winteractive '/wormhole receive/ { printf "sudo sh -c '"'"'/usr/lib/startos/scripts/prune-images $(SQFS_SIZE) && cd /media/startos/images && wormhole receive --accept-file %s && mv $(BASENAME).squashfs $(SQFS_SUM).rootfs && ln -rsf ./$(SQFS_SUM).rootfs ../config/current.rootfs && sync && reboot'"'"'\n", $$3 }' update: $(ALL_TARGETS) @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi @@ -169,6 +174,28 @@ update: $(ALL_TARGETS) $(MAKE) install REMOTE=$(REMOTE) SSHPASS=$(SSHPASS) DESTDIR=/media/startos/next PLATFORM=$(PLATFORM) $(call ssh,'sudo /media/startos/next/usr/lib/startos/scripts/chroot-and-upgrade --no-sync "apt-get install -y $(shell cat ./build/lib/depends)"') +update-startbox: core/target/$(ARCH)-unknown-linux-musl/release/startbox # only update binary (faster than full update) + @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi + $(call ssh,'sudo /usr/lib/startos/scripts/chroot-and-upgrade --create') + $(call cp,core/target/$(ARCH)-unknown-linux-musl/release/startbox,/media/startos/next/usr/bin/startbox) + $(call ssh,'sudo /media/startos/next/usr/lib/startos/scripts/chroot-and-upgrade --no-sync true') + +update-deb: results/$(BASENAME).deb # better than update, but only available from debian + @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi + $(call ssh,'sudo /usr/lib/startos/scripts/chroot-and-upgrade --create') + $(call mkdir,/media/startos/next/tmp/startos-deb) + $(call cp,results/$(BASENAME).deb,/media/startos/next/tmp/startos-deb/$(BASENAME).deb) + $(call ssh,'sudo /media/startos/next/usr/lib/startos/scripts/chroot-and-upgrade --no-sync "apt-get install -y --reinstall /tmp/startos-deb/$(BASENAME).deb"') + +update-squashfs: results/$(BASENAME).squashfs + @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi + $(eval SQFS_SUM := $(shell b3sum results/$(BASENAME).squashfs)) + $(eval SQFS_SIZE := $(shell du -s --bytes results/$(BASENAME).squashfs | awk '{print $$1}')) + $(call ssh,'/usr/lib/startos/scripts/prune-images $(SQFS_SIZE)') + $(call cp,results/$(BASENAME).squashfs,/media/startos/images/$(SQFS_SUM).rootfs) + $(call ssh,'sudo ln -rsf /media/startos/images/$(SQFS_SUM).rootfs /media/startos/config/current.rootfs') + $(call ssh,'sudo reboot') + emulate-reflash: $(ALL_TARGETS) @if [ -z "$(REMOTE)" ]; then >&2 echo "Must specify REMOTE" && false; fi $(call ssh,'sudo /usr/lib/startos/scripts/chroot-and-upgrade --create') diff --git a/assets/create-vm/step-1.png b/assets/create-vm/step-1.png new file mode 100644 index 0000000000000000000000000000000000000000..2dfafc25f28c9aed407ade6bba082031a7198b7b GIT binary patch literal 28070 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%0A~~67(4io63xsrHFma81p~rN%Iq5) z9($+E&Aa~BY=E?KTMR4K2Umbc@hVjtvCsh&ZanXLugPL>mjBv>vdj@?mJ{~d03L9_ zbs>pK7RKhLq6S$P0Y}?T)(21sPdxAp0TAIeHuoNF970P<+8b+Sy?soLbss!o{!g6#tF$FJ$xw=e^n<;YDT z-Ek{p*dvRt;a-ZprfMx=Of|%Dk{7E08V8Cv-4wPPrW)4Tf<>vRP90yDS~d>%@thPg zv7j0hk|IS7g$rz)hA_;Gk)0zUL2>sEL*YPHG@}@ZX5fd<+2c~|z%+%R`(vwa$B73Z z3~2DqMO)w&?P!4pi{U2kg>r!*)X;hy#2C~9;{JuvBgjhLYqfWTB6GHOe^eWE50)#gzjV28J>L7G1 z^$-RMQW=lgT6mCQM2|&3@Eh>Y_u0+0&F{cP`K~h^k8R#q>Ob14+j<4Mgh;R$Yg{s# zQ&6|ToAj@ zi`HwS-C#t{_%ss(?uFH{e4UOtrh%ch)R*jdSqM2}BvKoeG_(#0YZH=cWAed-k^=6L z_qf7{A;eW&kNdpQadEiz3PxMIX};DlHSD47bUu6ge%njzat5jGFtVT3$N%-4RyE!P zC7=>4(es23ckFQPwwL+aRAlbW76;PVB+uJVA*iun4em8+^5P=P+ zQ(Kx|FW7crZ}(@MsU-II&wMRVS0zRMNpUWsjq>IQkh6d5SiupH2+z@u4Qy{yFY3|z z_TQF4RgXe{5!J7Q$?7y4VGioafAZ!ZGwpxcu`gB#nwl@AgpQVBC4MVU|Cp2iBbiVj zie`P&6x-9avyVJ(^|UF_3qZe>wZTWj-X2OG#`bgX8tEK!@pr`mGXD1i;6ZrE5mjcv W^-BxB_J>~f^>JMbayj!ch^1o_5I!Z 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 DyPTO6 literal 0 HcmV?d00001 diff --git a/assets/create-vm/step-11.png b/assets/create-vm/step-11.png new file mode 100644 index 0000000000000000000000000000000000000000..322dd5394c9558979e09eafe00074dcefb0f5e80 GIT binary patch literal 54662 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 literal 0 HcmV?d00001 diff --git a/assets/create-vm/step-2.png b/assets/create-vm/step-2.png new file mode 100644 index 0000000000000000000000000000000000000000..020f3d7d178cf929b693778b1b69344519cddc13 GIT binary patch literal 47071 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 literal 0 HcmV?d00001 diff --git a/assets/create-vm/step-5.png b/assets/create-vm/step-5.png new file mode 100644 index 0000000000000000000000000000000000000000..d34cb16a73caeddcb13b67d29593d217812bd22f GIT binary patch literal 65075 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%0D% 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 Q2;iT%h>UQlpsvsV0Y+KGfB*mh literal 0 HcmV?d00001 diff --git a/assets/create-vm/step-7.png b/assets/create-vm/step-7.png new file mode 100644 index 0000000000000000000000000000000000000000..ad8eb9b81c5e54449f432fc300d14a1fc661057b GIT binary patch literal 65527 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 o+-~971$48EO_w6wAV_ye-qF|Jd(OG{ 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 literal 0 HcmV?d00001 diff --git a/assets/create-vm/step-9.png b/assets/create-vm/step-9.png new file mode 100644 index 0000000000000000000000000000000000000000..0427354903fa5fdb8e87527e84f4a1b001f4ae3b GIT binary patch literal 45452 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 echo 'Must be run as root' exit 1 @@ -77,20 +79,7 @@ umount /media/startos/next/boot if [ "$CHROOT_RES" -eq 0 ]; then if [ -h /media/startos/config/current.rootfs ] && [ -e /media/startos/config/current.rootfs ]; then - echo 'Pruning...' - current="$(readlink -f /media/startos/config/current.rootfs)" - needed=$(du -s --bytes /media/startos/next | awk '{print $1}') - while [[ "$(df -B1 --output=avail --sync /media/startos/images | tail -n1)" -lt "$needed" ]]; do - to_prune="$(ls -t1 /media/startos/images/*.rootfs | grep -v "$current" | tail -n1)" - if [ -e "$to_prune" ]; then - echo " Pruning $to_prune" - rm -rf "$to_prune" - else - >&2 echo "Not enough space and nothing to prune!" - exit 1 - fi - done - echo 'done.' + ${SOURCE_DIR}/prune-images $(du -s --bytes /media/startos/next | awk '{print $1}') fi echo 'Upgrading...' diff --git a/build/lib/scripts/prune-images b/build/lib/scripts/prune-images new file mode 100755 index 000000000..21861467f --- /dev/null +++ b/build/lib/scripts/prune-images @@ -0,0 +1,49 @@ +#!/bin/bash + +if [ "$UID" -ne 0 ]; then + >&2 echo 'Must be run as root' + exit 1 +fi + +POSITIONAL_ARGS=() + +while [[ $# -gt 0 ]]; do + case $1 in + -*|--*) + echo "Unknown option $1" + exit 1 + ;; + *) + POSITIONAL_ARGS+=("$1") # save positional arg + shift # past argument + ;; + esac +done + +set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters + +needed=$1 + +if [ -z "$needed" ]; then + >&2 echo "usage: $0 " + exit 1 +fi + +if [ -h /media/startos/config/current.rootfs ] && [ -e /media/startos/config/current.rootfs ]; then + echo 'Pruning...' + current="$(readlink -f /media/startos/config/current.rootfs)" + while [[ "$(df -B1 --output=avail --sync /media/startos/images | tail -n1)" -lt "$needed" ]]; do + to_prune="$(ls -t1 /media/startos/images/*.rootfs | grep -v "$current" | tail -n1)" + if [ -e "$to_prune" ]; then + echo " Pruning $to_prune" + rm -rf "$to_prune" + else + >&2 echo "Not enough space and nothing to prune!" + exit 1 + fi + done + echo 'done.' +else + >&2 echo 'No current.rootfs, not safe to prune' + exit 1 +fi \ No newline at end of file From c16d8a1da192af57a41098dab305330e0302d380 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 25 Jun 2024 20:58:24 -0600 Subject: [PATCH 18/20] fix setup wizard styles and remove diagnostic from angular.json (#2656) --- web/angular.json | 131 +----------------- .../src/app/pages/loading/loading.module.ts | 11 +- .../src/app/pages/loading/loading.page.html | 40 +++--- .../src/app/pages/loading/loading.page.scss | 9 ++ .../ui/src/app/pages/init/init.page.html | 2 +- 5 files changed, 42 insertions(+), 151 deletions(-) diff --git a/web/angular.json b/web/angular.json index 3a91dd00b..cf6c113a4 100644 --- a/web/angular.json +++ b/web/angular.json @@ -302,6 +302,7 @@ } ], "styles": [ + "node_modules/@taiga-ui/core/styles/taiga-ui-theme.less", "projects/shared/styles/variables.scss", "projects/shared/styles/global.scss", "projects/shared/styles/shared.scss", @@ -393,136 +394,6 @@ } } }, - "diagnostic-ui": { - "projectType": "application", - "schematics": {}, - "root": "projects/diagnostic-ui", - "sourceRoot": "projects/diagnostic-ui/src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/raw/diagnostic-ui", - "index": "projects/diagnostic-ui/src/index.html", - "main": "projects/diagnostic-ui/src/main.ts", - "polyfills": "projects/diagnostic-ui/src/polyfills.ts", - "tsConfig": "projects/diagnostic-ui/tsconfig.json", - "inlineStyleLanguage": "scss", - "assets": [ - { - "glob": "**/*", - "input": "projects/shared/assets", - "output": "assets" - }, - { - "glob": "**/*.svg", - "input": "node_modules/ionicons/dist/ionicons/svg", - "output": "./svg" - } - ], - "styles": [ - "projects/shared/styles/variables.scss", - "projects/shared/styles/global.scss", - "projects/shared/styles/shared.scss", - "projects/diagnostic-ui/src/styles.scss" - ], - "scripts": [] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "projects/diagnostic-ui/src/environments/environment.ts", - "with": "projects/diagnostic-ui/src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "budgets": [ - { - "type": "initial", - "maximumWarning": "2mb", - "maximumError": "5mb" - } - ] - }, - "ci": { - "progress": false - }, - "development": { - "buildOptimizer": false, - "optimization": false, - "vendorChunk": true, - "extractLicenses": false, - "sourceMap": true, - "namedChunks": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "diagnostic-ui:build" - }, - "configurations": { - "production": { - "browserTarget": "diagnostic-ui:build:production" - }, - "development": { - "browserTarget": "diagnostic-ui:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "diagnostic-ui:build" - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": [ - "projects/diagnostic-ui/src/**/*.ts", - "projects/diagnostic-ui/src/**/*.html" - ] - } - }, - "ionic-cordova-build": { - "builder": "@ionic/angular-toolkit:cordova-build", - "options": { - "browserTarget": "diagnostic-ui:build" - }, - "configurations": { - "production": { - "browserTarget": "diagnostic-ui:build:production" - } - } - }, - "ionic-cordova-serve": { - "builder": "@ionic/angular-toolkit:cordova-serve", - "options": { - "cordovaBuildTarget": "diagnostic-ui:ionic-cordova-build", - "devServerTarget": "diagnostic-ui:serve" - }, - "configurations": { - "production": { - "cordovaBuildTarget": "diagnostic-ui:ionic-cordova-build:production", - "devServerTarget": "diagnostic-ui:serve:production" - } - } - } - } - }, "marketplace": { "projectType": "library", "root": "projects/marketplace", diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.module.ts b/web/projects/setup-wizard/src/app/pages/loading/loading.module.ts index 2f3507941..498620697 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.module.ts +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.module.ts @@ -1,12 +1,17 @@ import { NgModule } from '@angular/core' import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { FormsModule } from '@angular/forms' +import { TuiProgressModule } from '@taiga-ui/kit' import { LoadingPage } from './loading.page' import { LoadingPageRoutingModule } from './loading-routing.module' +import { IonicModule } from '@ionic/angular' @NgModule({ - imports: [CommonModule, FormsModule, IonicModule, LoadingPageRoutingModule], + imports: [ + CommonModule, + IonicModule, + TuiProgressModule, + LoadingPageRoutingModule, + ], declarations: [LoadingPage], }) export class LoadingPageModule {} diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.page.html b/web/projects/setup-wizard/src/app/pages/loading/loading.page.html index 6c9ca41ab..94a666223 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.page.html +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.page.html @@ -1,17 +1,23 @@ -

-

- Setting up your server -

-
- Progress: {{ (progress.total * 100).toFixed(0) }}% -
- - -

{{ progress.message }}

-
+ + + + +
+

+ Setting up your server +

+
+ Progress: {{ (progress.total * 100).toFixed(0) }}% +
+ +

{{ progress.message }}

+
+
+
+
+
diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.page.scss b/web/projects/setup-wizard/src/app/pages/loading/loading.page.scss index e69de29bb..099f7084e 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.page.scss +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.page.scss @@ -0,0 +1,9 @@ +section { + border-radius: 0.25rem; + padding: 3rem; + margin: 2rem; + text-align: center; + background: #e0e0e0; + color: #333; + --tui-clear-inverse: rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/web/projects/ui/src/app/pages/init/init.page.html b/web/projects/ui/src/app/pages/init/init.page.html index bd3467bbb..5cd21bb07 100644 --- a/web/projects/ui/src/app/pages/init/init.page.html +++ b/web/projects/ui/src/app/pages/init/init.page.html @@ -2,7 +2,7 @@

Initializing StartOS

-
+
Progress: {{ (progress.total * 100).toFixed(0) }}%
From 822dd5e1004bacf836b098c4eba0b753e6005ef6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:03:01 -0600 Subject: [PATCH 19/20] Feature/UI sideload (#2658) * ui sideloading * remove subtlecrypto import * fix parser * misc fixes * allow docker pull during compat conversion --- Makefile | 12 +- container-runtime/package-lock.json | 81 +++- container-runtime/package.json | 4 +- core/Cargo.lock | 223 +++++++-- core/models/Cargo.toml | 2 +- core/models/src/errors.rs | 4 +- core/models/src/version.rs | 31 +- core/startos/Cargo.toml | 2 +- core/startos/src/account.rs | 8 + core/startos/src/backup/os.rs | 19 +- core/startos/src/backup/restore.rs | 8 +- core/startos/src/backup/target/mod.rs | 13 +- core/startos/src/context/cli.rs | 13 +- core/startos/src/context/config.rs | 5 +- core/startos/src/context/rpc.rs | 1 - core/startos/src/db/model/mod.rs | 1 + core/startos/src/db/model/package.rs | 2 +- core/startos/src/db/model/private.rs | 6 + core/startos/src/db/model/public.rs | 8 +- core/startos/src/developer/mod.rs | 5 +- core/startos/src/disk/mod.rs | 6 +- core/startos/src/disk/util.rs | 4 +- core/startos/src/firmware.rs | 5 +- core/startos/src/init.rs | 9 +- core/startos/src/install/mod.rs | 432 ++++++++++-------- core/startos/src/lib.rs | 8 +- core/startos/src/lxc/mod.rs | 4 +- core/startos/src/net/service_interface.rs | 2 - core/startos/src/net/static_server.rs | 9 +- core/startos/src/notifications.rs | 2 + core/startos/src/os_install/mod.rs | 12 +- core/startos/src/registry/asset.rs | 16 + core/startos/src/registry/device_info.rs | 2 +- core/startos/src/registry/mod.rs | 1 - core/startos/src/registry/os/asset/add.rs | 6 +- core/startos/src/registry/os/asset/get.rs | 5 +- core/startos/src/registry/os/asset/sign.rs | 3 +- core/startos/src/registry/os/index.rs | 2 +- core/startos/src/registry/os/version/mod.rs | 11 +- core/startos/src/registry/package/get.rs | 9 +- core/startos/src/registry/package/index.rs | 5 +- core/startos/src/registry/signer/sign/mod.rs | 1 - .../s9pk/merkle_archive/directory_contents.rs | 15 + core/startos/src/s9pk/merkle_archive/mod.rs | 7 +- .../src/s9pk/merkle_archive/source/http.rs | 9 +- .../src/s9pk/merkle_archive/source/mod.rs | 106 ++++- .../source/multi_cursor_file.rs | 39 +- .../startos/src/s9pk/merkle_archive/varint.rs | 14 +- core/startos/src/s9pk/mod.rs | 54 ++- core/startos/src/s9pk/rpc.rs | 60 ++- core/startos/src/s9pk/v1/manifest.rs | 14 +- core/startos/src/s9pk/v1/reader.rs | 5 +- core/startos/src/s9pk/v2/compat.rs | 184 +++----- core/startos/src/s9pk/v2/manifest.rs | 8 +- core/startos/src/s9pk/v2/mod.rs | 16 +- core/startos/src/s9pk/v2/pack.rs | 113 +++-- core/startos/src/service/mod.rs | 4 +- .../src/service/persistent_container.rs | 7 +- .../src/service/service_effect_handler.rs | 6 +- core/startos/src/service/service_map.rs | 15 +- core/startos/src/setup.rs | 5 +- core/startos/src/ssh.rs | 3 +- core/startos/src/system.rs | 3 +- core/startos/src/update/mod.rs | 4 +- core/startos/src/upload.rs | 129 +++++- core/startos/src/util/io.rs | 11 +- core/startos/src/util/mod.rs | 13 +- core/startos/src/util/rpc.rs | 5 +- core/startos/src/util/serde.rs | 27 +- core/startos/src/version/mod.rs | 51 ++- core/startos/src/version/v0_3_5.rs | 30 +- core/startos/src/version/v0_3_5_1.rs | 10 +- core/startos/src/version/v0_3_5_2.rs | 10 +- core/startos/src/version/v0_3_6.rs | 10 +- debian/postinst | 1 + package-lock.json | 6 + sdk/lib/emverLite/mod.ts | 4 +- sdk/lib/index.browser.ts | 1 + sdk/lib/index.ts | 1 + sdk/lib/osBindings/GetPackageParams.ts | 3 +- sdk/lib/osBindings/Manifest.ts | 2 +- sdk/lib/osBindings/PackageVersionInfo.ts | 3 +- sdk/lib/osBindings/ServerInfo.ts | 3 +- sdk/lib/s9pk/index.ts | 67 +++ .../s9pk/merkleArchive/directoryContents.ts | 80 ++++ sdk/lib/s9pk/merkleArchive/fileContents.ts | 24 + sdk/lib/s9pk/merkleArchive/index.ts | 167 +++++++ sdk/lib/s9pk/merkleArchive/varint.ts | 62 +++ sdk/lib/test/emverList.test.ts | 9 - sdk/lib/util/fileHelper.ts | 4 +- sdk/package-lock.json | 84 +++- sdk/package.json | 11 +- system-images/compat/src/config/mod.rs | 2 +- web/package-lock.json | 80 +++- web/package.json | 6 +- .../server-routes/sideload/sideload.page.ts | 56 ++- .../ui/src/app/services/api/api.types.ts | 5 +- .../app/services/api/embassy-api.service.ts | 4 +- .../services/api/embassy-live-api.service.ts | 8 +- .../services/api/embassy-mock-api.service.ts | 9 +- web/projects/ui/src/polyfills.ts | 7 +- 101 files changed, 1901 insertions(+), 797 deletions(-) create mode 100644 package-lock.json create mode 100644 sdk/lib/s9pk/index.ts create mode 100644 sdk/lib/s9pk/merkleArchive/directoryContents.ts create mode 100644 sdk/lib/s9pk/merkleArchive/fileContents.ts create mode 100644 sdk/lib/s9pk/merkleArchive/index.ts create mode 100644 sdk/lib/s9pk/merkleArchive/varint.ts diff --git a/Makefile b/Makefile index 5e038a08d..9a9a001b7 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ COMPAT_SRC := $(shell git ls-files system-images/compat/) UTILS_SRC := $(shell git ls-files system-images/utils/) BINFMT_SRC := $(shell git ls-files system-images/binfmt/) CORE_SRC := $(shell git ls-files core) $(shell git ls-files --recurse-submodules patch-db) web/dist/static web/patchdb-ui-seed.json $(GIT_HASH_FILE) -WEB_SHARED_SRC := $(shell git ls-files web/projects/shared) $(shell ls -p web/ | grep -v / | sed 's/^/web\//g') web/node_modules/.package-lock.json web/config.json patch-db/client/dist web/patchdb-ui-seed.json +WEB_SHARED_SRC := $(shell git ls-files web/projects/shared) $(shell ls -p web/ | grep -v / | sed 's/^/web\//g') web/node_modules/.package-lock.json web/config.json patch-db/client/dist web/patchdb-ui-seed.json sdk/dist WEB_UI_SRC := $(shell git ls-files web/projects/ui) WEB_SETUP_WIZARD_SRC := $(shell git ls-files web/projects/setup-wizard) WEB_INSTALL_WIZARD_SRC := $(shell git ls-files web/projects/install-wizard) @@ -262,15 +262,19 @@ web/node_modules/.package-lock.json: web/package.json sdk/dist npm --prefix web ci touch web/node_modules/.package-lock.json -web/dist/raw/ui: $(WEB_UI_SRC) $(WEB_SHARED_SRC) +web/.angular: patch-db/client/dist sdk/dist web/node_modules/.package-lock.json + rm -rf web/.angular + mkdir -p web/.angular + +web/dist/raw/ui: $(WEB_UI_SRC) $(WEB_SHARED_SRC) web/.angular npm --prefix web run build:ui touch web/dist/raw/ui -web/dist/raw/setup-wizard: $(WEB_SETUP_WIZARD_SRC) $(WEB_SHARED_SRC) +web/dist/raw/setup-wizard: $(WEB_SETUP_WIZARD_SRC) $(WEB_SHARED_SRC) web/.angular npm --prefix web run build:setup touch web/dist/raw/setup-wizard -web/dist/raw/install-wizard: $(WEB_INSTALL_WIZARD_SRC) $(WEB_SHARED_SRC) +web/dist/raw/install-wizard: $(WEB_INSTALL_WIZARD_SRC) $(WEB_SHARED_SRC) web/.angular npm --prefix web run build:install-wiz touch web/dist/raw/install-wizard diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 9b211c077..e63bec6a1 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -9,11 +9,13 @@ "version": "0.0.0", "dependencies": { "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "@start9labs/start-sdk": "file:../sdk/dist", "esbuild-plugin-resolve": "^2.0.0", "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "node-fetch": "^3.1.0", "ts-matches": "^5.5.1", "tslib": "^2.5.3", @@ -30,24 +32,27 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha1", + "version": "0.3.6-alpha5", "license": "MIT", "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", - "ts-matches": "^5.4.1" + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" }, "devDependencies": { - "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", - "@types/lodash": "^4.17.5", + "@types/lodash.merge": "^4.6.2", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "tsx": "^4.7.1", - "typescript": "^5.0.4", - "yaml": "^2.2.2" + "typescript": "^5.0.4" } }, "node_modules/@iarna/toml": { @@ -72,6 +77,28 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -1316,10 +1343,10 @@ "json-buffer": "3.0.1" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "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==" }, "node_modules/lowercase-keys": { "version": "2.0.0", @@ -2233,6 +2260,19 @@ "os-filter-obj": "^2.0.0" } }, + "@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "requires": { + "@noble/hashes": "1.4.0" + } + }, + "@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -2261,14 +2301,17 @@ "version": "file:../sdk/dist", "requires": { "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "@types/jest": "^29.4.0", - "@types/lodash": "^4.17.5", + "@types/lodash.merge": "^4.6.2", "isomorphic-fetch": "^3.0.0", "jest": "^29.4.3", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", - "ts-matches": "^5.4.1", + "ts-matches": "^5.5.1", "ts-node": "^10.9.1", "tsx": "^4.7.1", "typescript": "^5.0.4", @@ -2988,10 +3031,10 @@ "json-buffer": "3.0.1" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "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==" }, "lowercase-keys": { "version": "2.0.0", diff --git a/container-runtime/package.json b/container-runtime/package.json index 357c606fc..515f50ee7 100644 --- a/container-runtime/package.json +++ b/container-runtime/package.json @@ -18,10 +18,12 @@ "dependencies": { "@iarna/toml": "^2.2.5", "@start9labs/start-sdk": "file:../sdk/dist", + "@noble/hashes": "^1.4.0", + "@noble/curves": "^1.4.0", "esbuild-plugin-resolve": "^2.0.0", "filebrowser": "^1.0.0", "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "node-fetch": "^3.1.0", "ts-matches": "^5.5.1", "tslib": "^2.5.3", diff --git a/core/Cargo.lock b/core/Cargo.lock index 6db13b8cd..80c2dcc0e 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -166,6 +166,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.4" @@ -508,9 +514,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -521,16 +527,28 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" +[[package]] +name = "bitvec" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" +dependencies = [ + "funty 1.1.0", + "radium 0.5.3", + "tap", + "wyz 0.2.0", +] + [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", + "funty 2.0.0", + "radium 0.7.0", "tap", - "wyz", + "wyz 0.5.1", ] [[package]] @@ -540,7 +558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.4", "constant_time_eq", ] @@ -551,7 +569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" dependencies = [ "arrayref", - "arrayvec", + "arrayvec 0.7.4", "cc", "cfg-if", "constant_time_eq", @@ -624,9 +642,9 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" +checksum = "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d" dependencies = [ "jobserver", "libc", @@ -1051,7 +1069,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "futures-core", "libc", @@ -1216,7 +1234,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "709ade444d53896e60f6265660eb50480dd08b77bfc822e5dcc233b88b0b2fba" dependencies = [ - "bitvec", + "bitvec 1.0.1", "deku_derive", "no_std_io", "rustversion", @@ -1416,9 +1434,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" dependencies = [ "serde", ] @@ -1445,12 +1463,13 @@ dependencies = [ [[package]] name = "emver" -version = "0.1.7" -source = "git+https://github.com/Start9Labs/emver-rs.git#61cf0bc96711b4d6f3f30df8efef025e0cc02bad" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed260c4d7efaec031b9c4f6c4d3cf136e3df2bbfe50925800236f5e847f28704" dependencies = [ "either", "fp-core", - "nom", + "nom 6.1.2", "serde", ] @@ -1529,6 +1548,24 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exver" +version = "0.2.0" +source = "git+https://github.com/Start9Labs/exver-rs.git#29f52c1be18a0fe187670beac92822994b0d1949" +dependencies = [ + "either", + "emver", + "fp-core", + "getrandom 0.2.15", + "itertools 0.13.0", + "memchr", + "pest", + "pest_derive", + "serde", + "smallvec", + "yasi", +] + [[package]] name = "eyre" version = "0.6.12" @@ -1648,6 +1685,12 @@ dependencies = [ "itertools 0.8.2", ] +[[package]] +name = "funty" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" + [[package]] name = "funty" version = "2.0.0" @@ -1799,7 +1842,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crc", "log", "uuid", @@ -1913,7 +1956,7 @@ dependencies = [ "base64 0.21.7", "byteorder", "flate2", - "nom", + "nom 7.1.3", "num-traits", ] @@ -2461,6 +2504,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2637,6 +2689,19 @@ dependencies = [ "spin", ] +[[package]] +name = "lexical-core" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" +dependencies = [ + "arrayvec 0.5.2", + "bitflags 1.3.2", + "cfg-if", + "ryu", + "static_assertions", +] + [[package]] name = "libc" version = "0.2.155" @@ -2655,7 +2720,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -2731,7 +2796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c487024623ae38584610237dd1be8932bb2b324474b23c37a25f9fbe6bf5e9e" dependencies = [ "bincode", - "bitvec", + "bitvec 1.0.1", "serde", "serde-big-array", "thiserror", @@ -2821,7 +2886,7 @@ dependencies = [ "base64 0.21.7", "color-eyre", "ed25519-dalek 2.1.1", - "emver", + "exver", "ipnet", "lazy_static", "mbrman", @@ -2908,7 +2973,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "libc", ] @@ -2922,6 +2987,19 @@ dependencies = [ "memchr", ] +[[package]] +name = "nom" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" +dependencies = [ + "bitvec 0.19.6", + "funty 1.1.0", + "lexical-core", + "memchr", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -2958,9 +3036,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -3116,7 +3194,7 @@ version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -3309,6 +3387,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.68", +] + +[[package]] +name = "pest_meta" +version = "2.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" +dependencies = [ + "once_cell", + "pest", + "sha2 0.10.8", +] + [[package]] name = "petgraph" version = "0.6.5" @@ -3466,7 +3589,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.5.0", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -3552,6 +3675,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" + [[package]] name = "radium" version = "0.7.0" @@ -3697,7 +3826,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -3948,7 +4077,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -4133,7 +4262,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4198,9 +4327,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ "indexmap 2.2.6", "itoa", @@ -4459,7 +4588,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "nom", + "nom 7.1.3", "unicode_categories", ] @@ -4566,7 +4695,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "bytes", "chrono", @@ -4609,7 +4738,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "chrono", "crc", @@ -4765,7 +4894,7 @@ dependencies = [ "ed25519 2.2.3", "ed25519-dalek 1.0.1", "ed25519-dalek 2.1.1", - "emver", + "exver", "fd-lock-rs", "futures", "gpt", @@ -4799,7 +4928,7 @@ dependencies = [ "models", "new_mime_guess", "nix 0.27.1", - "nom", + "nom 7.1.3", "num", "num_enum", "once_cell", @@ -4861,6 +4990,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stderrlog" version = "0.5.4" @@ -5113,9 +5248,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -5634,6 +5769,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unarray" version = "0.1.4" @@ -6121,6 +6262,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wyz" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" + [[package]] name = "wyz" version = "0.5.1" diff --git a/core/models/Cargo.toml b/core/models/Cargo.toml index 76c66b4f2..44295745d 100644 --- a/core/models/Cargo.toml +++ b/core/models/Cargo.toml @@ -12,7 +12,7 @@ color-eyre = "0.6.2" ed25519-dalek = { version = "2.0.0", features = ["serde"] } lazy_static = "1.4" mbrman = "0.5.2" -emver = { version = "0.1", git = "https://github.com/Start9Labs/emver-rs.git", features = [ +exver = { version = "0.2.0", git = "https://github.com/Start9Labs/exver-rs.git", features = [ "serde", ] } ipnet = "2.8.0" diff --git a/core/models/src/errors.rs b/core/models/src/errors.rs index 95416ec80..8bbc705ee 100644 --- a/core/models/src/errors.rs +++ b/core/models/src/errors.rs @@ -242,8 +242,8 @@ impl From for Error { Error::new(e, ErrorKind::Utf8) } } -impl From for Error { - fn from(e: emver::ParseError) -> Self { +impl From for Error { + fn from(e: exver::ParseError) -> Self { Error::new(e, ErrorKind::ParseVersion) } } diff --git a/core/models/src/version.rs b/core/models/src/version.rs index 48871e3a1..f0c7b19ae 100644 --- a/core/models/src/version.rs +++ b/core/models/src/version.rs @@ -8,14 +8,14 @@ use ts_rs::TS; #[derive(Debug, Clone, TS)] #[ts(type = "string", rename = "Version")] pub struct VersionString { - version: emver::Version, + version: exver::ExtendedVersion, string: String, } impl VersionString { pub fn as_str(&self) -> &str { self.string.as_str() } - pub fn into_version(self) -> emver::Version { + pub fn into_version(self) -> exver::ExtendedVersion { self.version } } @@ -25,7 +25,7 @@ impl std::fmt::Display for VersionString { } } impl std::str::FromStr for VersionString { - type Err = ::Err; + type Err = ::Err; fn from_str(s: &str) -> Result { Ok(VersionString { string: s.to_owned(), @@ -33,32 +33,32 @@ impl std::str::FromStr for VersionString { }) } } -impl From for VersionString { - fn from(v: emver::Version) -> Self { +impl From for VersionString { + fn from(v: exver::ExtendedVersion) -> Self { VersionString { string: v.to_string(), version: v, } } } -impl From for emver::Version { +impl From for exver::ExtendedVersion { fn from(v: VersionString) -> Self { v.version } } impl Default for VersionString { fn default() -> Self { - Self::from(emver::Version::default()) + Self::from(exver::ExtendedVersion::default()) } } impl Deref for VersionString { - type Target = emver::Version; + type Target = exver::ExtendedVersion; fn deref(&self) -> &Self::Target { &self.version } } -impl AsRef for VersionString { - fn as_ref(&self) -> &emver::Version { +impl AsRef for VersionString { + fn as_ref(&self) -> &exver::ExtendedVersion { &self.version } } @@ -80,7 +80,13 @@ impl PartialOrd for VersionString { } impl Ord for VersionString { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.version.cmp(&other.version) + self.version.partial_cmp(&other.version).unwrap_or_else(|| { + match (self.version.flavor(), other.version.flavor()) { + (None, Some(_)) => std::cmp::Ordering::Greater, + (Some(_), None) => std::cmp::Ordering::Less, + (a, b) => a.cmp(&b), + } + }) } } impl Hash for VersionString { @@ -94,7 +100,8 @@ impl<'de> Deserialize<'de> for VersionString { D: Deserializer<'de>, { let string = String::deserialize(deserializer)?; - let version = emver::Version::from_str(&string).map_err(::serde::de::Error::custom)?; + let version = + exver::ExtendedVersion::from_str(&string).map_err(::serde::de::Error::custom)?; Ok(Self { string, version }) } } diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index 401ee66a8..d2304fedb 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -86,7 +86,7 @@ ed25519-dalek = { version = "2.1.1", features = [ "pkcs8", ] } ed25519-dalek-v1 = { package = "ed25519-dalek", version = "1" } -emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", features = [ +exver = { version = "0.2.0", git = "https://github.com/Start9Labs/exver-rs.git", features = [ "serde", ] } fd-lock-rs = "0.1.4" diff --git a/core/startos/src/account.rs b/core/startos/src/account.rs index e074d301d..9e755342f 100644 --- a/core/startos/src/account.rs +++ b/core/startos/src/account.rs @@ -28,6 +28,7 @@ pub struct AccountInfo { pub root_ca_key: PKey, pub root_ca_cert: X509, pub ssh_key: ssh_key::PrivateKey, + pub compat_s9pk_key: ed25519_dalek::SigningKey, } impl AccountInfo { pub fn new(password: &str, start_time: SystemTime) -> Result { @@ -39,6 +40,7 @@ impl AccountInfo { let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random( &mut rand::thread_rng(), )); + let compat_s9pk_key = ed25519_dalek::SigningKey::generate(&mut rand::thread_rng()); Ok(Self { server_id, hostname, @@ -47,6 +49,7 @@ impl AccountInfo { root_ca_key, root_ca_cert, ssh_key, + compat_s9pk_key, }) } @@ -61,6 +64,7 @@ impl AccountInfo { let root_ca_key = cert_store.as_root_key().de()?.0; let root_ca_cert = cert_store.as_root_cert().de()?.0; let ssh_key = db.as_private().as_ssh_privkey().de()?.0; + let compat_s9pk_key = db.as_private().as_compat_s9pk_key().de()?.0; Ok(Self { server_id, @@ -70,6 +74,7 @@ impl AccountInfo { root_ca_key, root_ca_cert, ssh_key, + compat_s9pk_key, }) } @@ -92,6 +97,9 @@ impl AccountInfo { db.as_private_mut() .as_ssh_privkey_mut() .ser(Pem::new_ref(&self.ssh_key))?; + db.as_private_mut() + .as_compat_s9pk_key_mut() + .ser(Pem::new_ref(&self.compat_s9pk_key))?; let key_store = db.as_private_mut().as_key_store_mut(); key_store.as_onion_mut().insert_key(&self.tor_key)?; let cert_store = key_store.as_local_certs_mut(); diff --git a/core/startos/src/backup/os.rs b/core/startos/src/backup/os.rs index 6848473a7..7c8119e79 100644 --- a/core/startos/src/backup/os.rs +++ b/core/startos/src/backup/os.rs @@ -85,6 +85,7 @@ impl OsBackupV0 { ssh_key::Algorithm::Ed25519, )?, tor_key: TorSecretKeyV3::from(self.tor_key.0), + compat_s9pk_key: ed25519_dalek::SigningKey::generate(&mut rand::thread_rng()), }, ui: self.ui, }) @@ -113,6 +114,7 @@ impl OsBackupV1 { root_ca_cert: self.root_ca_cert.0, ssh_key: ssh_key::PrivateKey::from(Ed25519Keypair::from_seed(&self.net_key.0)), tor_key: TorSecretKeyV3::from(ed25519_expand_key(&self.net_key.0)), + compat_s9pk_key: ed25519_dalek::SigningKey::from_bytes(&self.net_key), }, ui: self.ui, } @@ -124,13 +126,14 @@ impl OsBackupV1 { #[serde(rename = "kebab-case")] struct OsBackupV2 { - server_id: String, // uuidv4 - hostname: String, // - - root_ca_key: Pem>, // PEM Encoded OpenSSL Key - root_ca_cert: Pem, // PEM Encoded OpenSSL X509 Certificate - ssh_key: Pem, // PEM Encoded OpenSSH Key - tor_key: TorSecretKeyV3, // Base64 Encoded Ed25519 Expanded Secret Key - ui: Value, // JSON Value + server_id: String, // uuidv4 + hostname: String, // - + root_ca_key: Pem>, // PEM Encoded OpenSSL Key + root_ca_cert: Pem, // PEM Encoded OpenSSL X509 Certificate + ssh_key: Pem, // PEM Encoded OpenSSH Key + tor_key: TorSecretKeyV3, // Base64 Encoded Ed25519 Expanded Secret Key + compat_s9pk_key: Pem, // PEM Encoded ED25519 Key + ui: Value, // JSON Value } impl OsBackupV2 { fn project(self) -> OsBackup { @@ -143,6 +146,7 @@ impl OsBackupV2 { root_ca_cert: self.root_ca_cert.0, ssh_key: self.ssh_key.0, tor_key: self.tor_key, + compat_s9pk_key: self.compat_s9pk_key.0, }, ui: self.ui, } @@ -155,6 +159,7 @@ impl OsBackupV2 { root_ca_cert: Pem(backup.account.root_ca_cert.clone()), ssh_key: Pem(backup.account.ssh_key.clone()), tor_key: backup.account.tor_key.clone(), + compat_s9pk_key: Pem(backup.account.compat_s9pk_key.clone()), ui: backup.ui.clone(), } } diff --git a/core/startos/src/backup/restore.rs b/core/startos/src/backup/restore.rs index 556f750ec..3f0aacbe8 100644 --- a/core/startos/src/backup/restore.rs +++ b/core/startos/src/backup/restore.rs @@ -156,16 +156,14 @@ async fn restore_packages( let mut tasks = BTreeMap::new(); for id in ids { let backup_dir = backup_guard.clone().package_backup(&id); + let s9pk_path = backup_dir.path().join(&id).with_extension("s9pk"); let task = ctx .services .install( ctx.clone(), - S9pk::open( - backup_dir.path().join(&id).with_extension("s9pk"), - Some(&id), - ) - .await?, + || S9pk::open(s9pk_path, Some(&id)), Some(backup_dir), + None, ) .await?; tasks.insert(id, task); diff --git a/core/startos/src/backup/target/mod.rs b/core/startos/src/backup/target/mod.rs index 2c7a28d94..f1273aa6a 100644 --- a/core/startos/src/backup/target/mod.rs +++ b/core/startos/src/backup/target/mod.rs @@ -7,6 +7,7 @@ use clap::Parser; use color_eyre::eyre::eyre; use digest::generic_array::GenericArray; use digest::OutputSizeUser; +use exver::Version; use models::PackageId; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; @@ -194,7 +195,7 @@ pub async fn list(ctx: RpcContext) -> Result>, pub package_backups: BTreeMap, } @@ -204,7 +205,7 @@ pub struct BackupInfo { pub struct PackageBackupInfo { pub title: String, pub version: VersionString, - pub os_version: VersionString, + pub os_version: Version, pub timestamp: DateTime, } @@ -223,9 +224,9 @@ fn display_backup_info(params: WithIoFormat, info: BackupInfo) { "TIMESTAMP", ]); table.add_row(row![ - "EMBASSY OS", - info.version.as_str(), - info.version.as_str(), + "StartOS", + &info.version.to_string(), + &info.version.to_string(), &if let Some(ts) = &info.timestamp { ts.to_string() } else { @@ -236,7 +237,7 @@ fn display_backup_info(params: WithIoFormat, info: BackupInfo) { let row = row![ &*id, info.version.as_str(), - info.os_version.as_str(), + &info.os_version.to_string(), &info.timestamp.to_string(), ]; table.add_row(row); diff --git a/core/startos/src/context/cli.rs b/core/startos/src/context/cli.rs index d560d575d..0eca1d2c2 100644 --- a/core/startos/src/context/cli.rs +++ b/core/startos/src/context/cli.rs @@ -43,7 +43,9 @@ impl Drop for CliContextSeed { std::fs::create_dir_all(&parent_dir).unwrap(); } let mut writer = fd_lock_rs::FdLock::lock( - File::create(&tmp).unwrap(), + File::create(&tmp) + .with_ctx(|_| (ErrorKind::Filesystem, &tmp)) + .unwrap(), fd_lock_rs::LockType::Exclusive, true, ) @@ -80,9 +82,12 @@ impl CliContext { }); let cookie_store = Arc::new(CookieStoreMutex::new({ let mut store = if cookie_path.exists() { - CookieStore::load_json(BufReader::new(File::open(&cookie_path)?)) - .map_err(|e| eyre!("{}", e)) - .with_kind(crate::ErrorKind::Deserialization)? + CookieStore::load_json(BufReader::new( + File::open(&cookie_path) + .with_ctx(|_| (ErrorKind::Filesystem, cookie_path.display()))?, + )) + .map_err(|e| eyre!("{}", e)) + .with_kind(crate::ErrorKind::Deserialization)? } else { CookieStore::default() }; diff --git a/core/startos/src/context/config.rs b/core/startos/src/context/config.rs index 7b4865301..e02648919 100644 --- a/core/startos/src/context/config.rs +++ b/core/startos/src/context/config.rs @@ -37,7 +37,10 @@ pub trait ContextConfig: DeserializeOwned + Default { .map(|f| f.parse()) .transpose()? .unwrap_or_default(); - format.from_reader(File::open(path)?) + format.from_reader( + File::open(path.as_ref()) + .with_ctx(|_| (ErrorKind::Filesystem, path.as_ref().display()))?, + ) } fn load_path_rec(&mut self, path: Option>) -> Result<(), Error> { if let Some(path) = path.filter(|p| p.as_ref().exists()) { diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index cf758e0fb..4920a6223 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -14,7 +14,6 @@ use rpc_toolkit::{CallRemote, Context, Empty}; use tokio::sync::{broadcast, Mutex, RwLock}; use tokio::time::Instant; use tracing::instrument; -use url::Url; use super::setup::CURRENT_SECRET; use crate::account::AccountInfo; diff --git a/core/startos/src/db/model/mod.rs b/core/startos/src/db/model/mod.rs index cae1bfe51..678f7e5fb 100644 --- a/core/startos/src/db/model/mod.rs +++ b/core/startos/src/db/model/mod.rs @@ -40,6 +40,7 @@ impl Database { notifications: Notifications::new(), cifs: CifsTargets::new(), package_stores: BTreeMap::new(), + compat_s9pk_key: Pem(account.compat_s9pk_key.clone()), }, // TODO }) } diff --git a/core/startos/src/db/model/package.rs b/core/startos/src/db/model/package.rs index 8bb5a9517..22d6440bb 100644 --- a/core/startos/src/db/model/package.rs +++ b/core/startos/src/db/model/package.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use chrono::{DateTime, Utc}; -use emver::VersionRange; +use exver::VersionRange; use imbl_value::InternedString; use models::{ActionId, DataUrl, HealthCheckId, HostId, PackageId, ServiceInterfaceId}; use patch_db::json_ptr::JsonPointer; diff --git a/core/startos/src/db/model/private.rs b/core/startos/src/db/model/private.rs index 2b8c55dbd..c57364fc3 100644 --- a/core/startos/src/db/model/private.rs +++ b/core/startos/src/db/model/private.rs @@ -19,6 +19,8 @@ use crate::util::serde::Pem; pub struct Private { pub key_store: KeyStore, pub password: String, // argon2 hash + #[serde(default = "generate_compat_key")] + pub compat_s9pk_key: Pem, pub ssh_privkey: Pem, pub ssh_pubkeys: SshKeys, pub available_ports: AvailablePorts, @@ -28,3 +30,7 @@ pub struct Private { #[serde(default)] pub package_stores: BTreeMap, } + +fn generate_compat_key() -> Pem { + Pem(ed25519_dalek::SigningKey::generate(&mut rand::thread_rng())) +} diff --git a/core/startos/src/db/model/public.rs b/core/startos/src/db/model/public.rs index e5257f2a4..5f8dc029a 100644 --- a/core/startos/src/db/model/public.rs +++ b/core/startos/src/db/model/public.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::net::{Ipv4Addr, Ipv6Addr}; use chrono::{DateTime, Utc}; -use emver::VersionRange; +use exver::{Version, VersionRange}; use imbl_value::InternedString; use ipnet::{Ipv4Net, Ipv6Net}; use isocountry::CountryCode; @@ -21,7 +21,6 @@ use crate::net::utils::{get_iface_ipv4_addr, get_iface_ipv6_addr}; use crate::prelude::*; use crate::progress::FullProgress; use crate::util::cpupower::Governor; -use crate::util::VersionString; use crate::version::{Current, VersionT}; use crate::{ARCH, PLATFORM}; @@ -43,7 +42,7 @@ impl Public { arch: get_arch(), platform: get_platform(), id: account.server_id.clone(), - version: Current::new().semver().into(), + version: Current::new().semver(), hostname: account.hostname.no_dot_host_name(), last_backup: None, eos_version_compat: Current::new().compat().clone(), @@ -109,7 +108,8 @@ pub struct ServerInfo { pub platform: InternedString, pub id: String, pub hostname: String, - pub version: VersionString, + #[ts(type = "string")] + pub version: Version, #[ts(type = "string | null")] pub last_backup: Option>, #[ts(type = "string")] diff --git a/core/startos/src/developer/mod.rs b/core/startos/src/developer/mod.rs index 79a875dfc..4a2a4c3df 100644 --- a/core/startos/src/developer/mod.rs +++ b/core/startos/src/developer/mod.rs @@ -8,8 +8,8 @@ use ed25519_dalek::{SigningKey, VerifyingKey}; use tracing::instrument; use crate::context::CliContext; +use crate::prelude::*; use crate::util::serde::Pem; -use crate::{Error, ResultExt}; #[instrument(skip_all)] pub fn init(ctx: CliContext) -> Result<(), Error> { @@ -26,7 +26,8 @@ pub fn init(ctx: CliContext) -> Result<(), Error> { secret_key: secret.to_bytes(), public_key: Some(PublicKeyBytes(VerifyingKey::from(&secret).to_bytes())), }; - let mut dev_key_file = File::create(&ctx.developer_key_path)?; + let mut dev_key_file = File::create(&ctx.developer_key_path) + .with_ctx(|_| (ErrorKind::Filesystem, ctx.developer_key_path.display()))?; dev_key_file.write_all( keypair_bytes .to_pkcs8_pem(base64ct::LineEnding::default()) diff --git a/core/startos/src/disk/mod.rs b/core/startos/src/disk/mod.rs index 705a34f98..d7ce5f766 100644 --- a/core/startos/src/disk/mod.rs +++ b/core/startos/src/disk/mod.rs @@ -102,10 +102,10 @@ fn display_disk_info(params: WithIoFormat, args: Vec) { } else { "N/A" }, - if let Some(eos) = part.start_os.as_ref() { - eos.version.as_str() + &if let Some(eos) = part.start_os.as_ref() { + eos.version.to_string() } else { - "N/A" + "N/A".to_owned() }, ]; table.add_row(row); diff --git a/core/startos/src/disk/util.rs b/core/startos/src/disk/util.rs index a98c52418..d3f2a8d27 100644 --- a/core/startos/src/disk/util.rs +++ b/core/startos/src/disk/util.rs @@ -20,7 +20,7 @@ use super::mount::guard::TmpMountGuard; use crate::disk::mount::guard::GenericMountGuard; use crate::disk::OsPartitionInfo; use crate::util::serde::IoFormat; -use crate::util::{Invoke, VersionString}; +use crate::util::Invoke; use crate::{Error, ResultExt as _}; #[derive(Clone, Copy, Debug, Deserialize, Serialize)] @@ -56,7 +56,7 @@ pub struct PartitionInfo { #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct EmbassyOsRecoveryInfo { - pub version: VersionString, + pub version: exver::Version, pub full: bool, pub password_hash: Option, pub wrapped_key: Option, diff --git a/core/startos/src/firmware.rs b/core/startos/src/firmware.rs index a9d5ced79..7e7b9d70f 100644 --- a/core/startos/src/firmware.rs +++ b/core/startos/src/firmware.rs @@ -3,13 +3,12 @@ use std::path::Path; use async_compression::tokio::bufread::GzipDecoder; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use tokio::io::BufReader; use tokio::process::Command; use crate::disk::fsck::RequiresReboot; use crate::prelude::*; -use crate::progress::PhaseProgressTrackerHandle; +use crate::util::io::open_file; use crate::util::Invoke; use crate::PLATFORM; @@ -134,7 +133,7 @@ pub async fn update_firmware(firmware: Firmware) -> Result<(), Error> { .invoke(ErrorKind::Filesystem) .await?; let mut rdr = if tokio::fs::metadata(&firmware_path).await.is_ok() { - GzipDecoder::new(BufReader::new(File::open(&firmware_path).await?)) + GzipDecoder::new(BufReader::new(open_file(&firmware_path).await?)) } else { return Err(Error::new( eyre!("Firmware {id}.rom.gz not found in {firmware_dir:?}"), diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index cdc444c32..fdc38b60d 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -5,7 +5,7 @@ use std::os::unix::fs::PermissionsExt; use std::path::Path; use std::time::{Duration, SystemTime}; -use axum::extract::ws::{self, CloseFrame}; +use axum::extract::ws::{self}; use color_eyre::eyre::eyre; use futures::{StreamExt, TryStreamExt}; use itertools::Itertools; @@ -31,7 +31,7 @@ use crate::progress::{ }; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::ssh::SSH_AUTHORIZED_KEYS_FILE; -use crate::util::io::IOHook; +use crate::util::io::{create_file, IOHook}; use crate::util::net::WebSocketExt; use crate::util::{cpupower, Invoke}; use crate::Error; @@ -138,10 +138,7 @@ pub async fn init_postgres(datadir: impl AsRef) -> Result<(), Error> { old_version -= 1; let old_datadir = db_dir.join(old_version.to_string()); if tokio::fs::metadata(&old_datadir).await.is_ok() { - tokio::fs::File::create(&incomplete_path) - .await? - .sync_all() - .await?; + create_file(&incomplete_path).await?.sync_all().await?; Command::new("pg_upgradecluster") .arg(old_version.to_string()) .arg("main") diff --git a/core/startos/src/install/mod.rs b/core/startos/src/install/mod.rs index 5d50da27d..18591dc8d 100644 --- a/core/startos/src/install/mod.rs +++ b/core/startos/src/install/mod.rs @@ -1,21 +1,21 @@ +use std::ops::Deref; use std::path::PathBuf; -use std::sync::Arc; use std::time::Duration; use clap::builder::ValueParserFactory; use clap::{value_parser, CommandFactory, FromArgMatches, Parser}; use color_eyre::eyre::eyre; -use emver::VersionRange; -use futures::StreamExt; -use imbl_value::InternedString; +use exver::VersionRange; +use futures::{AsyncWriteExt, StreamExt}; +use imbl_value::{json, InternedString}; use itertools::Itertools; -use patch_db::json_ptr::JsonPointer; +use models::VersionString; use reqwest::header::{HeaderMap, CONTENT_LENGTH}; use reqwest::Url; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::HandlerArgs; +use rustyline_async::ReadlineEvent; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; use tokio::sync::oneshot; use tracing::instrument; use ts_rs::TS; @@ -23,13 +23,14 @@ use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::db::model::package::{ManifestPreference, PackageState, PackageStateMatchModelRef}; use crate::prelude::*; -use crate::progress::{FullProgress, PhasedProgressBar}; +use crate::progress::{FullProgress, FullProgressTracker, PhasedProgressBar}; +use crate::registry::context::{RegistryContext, RegistryUrlParams}; +use crate::registry::package::get::GetPackageResponse; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::s9pk::manifest::PackageId; -use crate::s9pk::merkle_archive::source::http::HttpSource; -use crate::s9pk::S9pk; use crate::upload::upload; use crate::util::clap::FromStrParser; +use crate::util::io::open_file; use crate::util::net::WebSocketExt; use crate::util::Never; @@ -38,32 +39,33 @@ pub const PKG_PUBLIC_DIR: &str = "package-data/public"; pub const PKG_WASM_DIR: &str = "package-data/wasm"; // #[command(display(display_serializable))] -pub async fn list(ctx: RpcContext) -> Result { - Ok(ctx.db.peek().await.as_public().as_package_data().as_entries()? +pub async fn list(ctx: RpcContext) -> Result, Error> { + Ok(ctx + .db + .peek() + .await + .as_public() + .as_package_data() + .as_entries()? .iter() .filter_map(|(id, pde)| { let status = match pde.as_state_info().as_match() { - PackageStateMatchModelRef::Installed(_) => { - "installed" - } - PackageStateMatchModelRef::Installing(_) => { - "installing" - } - PackageStateMatchModelRef::Updating(_) => { - "updating" - } - PackageStateMatchModelRef::Restoring(_) => { - "restoring" - } - PackageStateMatchModelRef::Removing(_) => { - "removing" - } - PackageStateMatchModelRef::Error(_) => { - "error" - } + PackageStateMatchModelRef::Installed(_) => "installed", + PackageStateMatchModelRef::Installing(_) => "installing", + PackageStateMatchModelRef::Updating(_) => "updating", + PackageStateMatchModelRef::Restoring(_) => "restoring", + PackageStateMatchModelRef::Removing(_) => "removing", + PackageStateMatchModelRef::Error(_) => "error", }; - serde_json::to_value(json!({ "status": status, "id": id.clone(), "version": pde.as_state_info().as_manifest(ManifestPreference::Old).as_version().de().ok()?})) - .ok() + Some(json!({ + "status": status, + "id": id.clone(), + "version": pde.as_state_info() + .as_manifest(ManifestPreference::Old) + .as_version() + .de() + .ok()? + })) }) .collect()) } @@ -107,65 +109,57 @@ impl std::fmt::Display for MinMax { } } -#[derive(Deserialize, Serialize, Parser, TS)] +#[derive(Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] pub struct InstallParams { + #[ts(type = "string")] + registry: Url, id: PackageId, - #[arg(short = 'm', long = "marketplace-url")] - #[ts(type = "string | null")] - registry: Option, - #[arg(short = 'v', long = "version-spec")] - version_spec: Option, - #[arg(long = "version-priority")] - version_priority: Option, + version: VersionString, } -// #[command( -// custom_cli(cli_install(async, context(CliContext))), -// )] #[instrument(skip_all)] pub async fn install( ctx: RpcContext, InstallParams { - id, registry, - version_spec, - version_priority, + id, + version, }: InstallParams, ) -> Result<(), Error> { - let version_str = match &version_spec { - None => "*", - Some(v) => &*v, - }; - let version: VersionRange = version_str.parse()?; - let registry = registry.unwrap_or_else(|| crate::DEFAULT_MARKETPLACE.parse().unwrap()); - let version_priority = version_priority.unwrap_or_default(); - let s9pk = S9pk::deserialize( - &Arc::new( - HttpSource::new( - ctx.client.clone(), - format!( - "{}/package/v0/{}.s9pk?spec={}&version-priority={}", - registry, id, version, version_priority, - ) - .parse()?, - ) - .await?, - ), - None, // TODO - ) - .await?; + let package: GetPackageResponse = from_value( + ctx.call_remote_with::( + "package.get", + json!({ + "id": id, + "version": VersionRange::exactly(version.deref().clone()), + }), + RegistryUrlParams { + registry: registry.clone(), + }, + ) + .await?, + )?; - ensure_code!( - &s9pk.as_manifest().id == &id, - ErrorKind::ValidateS9pk, - "manifest.id does not match expected" - ); + let asset = &package + .best + .get(&version) + .ok_or_else(|| { + Error::new( + eyre!("{id}@{version} not found on {registry}"), + ErrorKind::NotFound, + ) + })? + .s9pk; let download = ctx .services - .install(ctx.clone(), s9pk, None::) + .install( + ctx.clone(), + || asset.deserialize_s9pk(ctx.client.clone()), + None::, + None, + ) .await?; tokio::spawn(async move { download.await?.await }); @@ -193,113 +187,74 @@ pub async fn sideload( SideloadParams { session }: SideloadParams, ) -> Result { let (upload, file) = upload(&ctx, session.clone()).await?; - let (id_send, id_recv) = oneshot::channel(); let (err_send, err_recv) = oneshot::channel(); let progress = Guid::new(); - let db = ctx.db.clone(); - let mut sub = db - .subscribe( - "/package-data/{id}/install-progress" - .parse::() - .with_kind(ErrorKind::Database)?, - ) - .await; - ctx.rpc_continuations.add( - progress.clone(), - RpcContinuation::ws_authed(&ctx, session, - |mut ws| { - use axum::extract::ws::Message; - async move { - if let Err(e) = async { - let id = match id_recv.await.map_err(|_| { - Error::new( - eyre!("Could not get id to watch progress"), - ErrorKind::Cancelled, - ) - }).and_then(|a|a) { - Ok(a) => a, - Err(e) =>{ ws.send(Message::Text( - serde_json::to_string(&Err::<(), _>(RpcError::from(e.clone_output()))) - .with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; - return Err(e); - } - }; - tokio::select! { - res = async { - while let Some(_) = sub.recv().await { - ws.send(Message::Text( - serde_json::to_string(&if let Some(p) = db - .peek() - .await - .as_public() - .as_package_data() - .as_idx(&id) - .and_then(|e| e.as_state_info().as_installing_info()).map(|i| i.as_progress()) - { - Ok::<_, ()>(p.de()?) - } else { - let mut p = FullProgress::new(); - p.overall.complete(); - Ok(p) - }) - .with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; - } - Ok::<_, Error>(()) - } => res?, - err = err_recv => { - if let Ok(e) = err { - ws.send(Message::Text( - serde_json::to_string(&Err::<(), _>(e)) - .with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; + let progress_tracker = FullProgressTracker::new(); + let mut progress_listener = progress_tracker.stream(Some(Duration::from_millis(200))); + ctx.rpc_continuations + .add( + progress.clone(), + RpcContinuation::ws_authed( + &ctx, + session, + |mut ws| { + use axum::extract::ws::Message; + async move { + if let Err(e) = async { + tokio::select! { + res = async { + while let Some(progress) = progress_listener.next().await { + ws.send(Message::Text( + serde_json::to_string(&Ok::<_, ()>(progress)) + .with_kind(ErrorKind::Serialization)?, + )) + .await + .with_kind(ErrorKind::Network)?; + } + Ok::<_, Error>(()) + } => res?, + err = err_recv => { + if let Ok(e) = err { + ws.send(Message::Text( + serde_json::to_string(&Err::<(), _>(e)) + .with_kind(ErrorKind::Serialization)?, + )) + .await + .with_kind(ErrorKind::Network)?; + } } } + + ws.normal_close("complete").await?; + + Ok::<_, Error>(()) + } + .await + { + tracing::error!("Error tracking sideload progress: {e}"); + tracing::debug!("{e:?}"); } - - ws.normal_close("complete").await?; - - Ok::<_, Error>(()) } - .await - { - tracing::error!("Error tracking sideload progress: {e}"); - tracing::debug!("{e:?}"); - } - } - }, - Duration::from_secs(600), - ), - ) - .await; + }, + Duration::from_secs(600), + ), + ) + .await; tokio::spawn(async move { if let Err(e) = async { - match S9pk::deserialize( - &file, None, // TODO - ) - .await - { - Ok(s9pk) => { - let _ = id_send.send(Ok(s9pk.as_manifest().id.clone())); - ctx.services - .install(ctx.clone(), s9pk, None::) - .await? - .await? - .await?; - file.delete().await - } - Err(e) => { - let _ = id_send.send(Err(e.clone_output())); - return Err(e); - } - } + let key = ctx.db.peek().await.into_private().into_compat_s9pk_key(); + + ctx.services + .install( + ctx.clone(), + || crate::s9pk::load(file.clone(), || Ok(key.de()?.0), Some(&progress_tracker)), + None::, + Some(progress_tracker.clone()), + ) + .await? + .await? + .await?; + file.delete().await } .await { @@ -311,10 +266,16 @@ pub async fn sideload( Ok(SideloadResponse { upload, progress }) } +#[derive(Deserialize, Serialize, Parser)] +pub struct QueryPackageParams { + id: PackageId, + version: Option, +} + #[derive(Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum CliInstallParams { - Marketplace(InstallParams), + Marketplace(QueryPackageParams), Sideload(PathBuf), } impl CommandFactory for CliInstallParams { @@ -328,14 +289,19 @@ impl CommandFactory for CliInstallParams { .required_unless_present("id") .value_parser(value_parser!(PathBuf)), ) - .args(InstallParams::command().get_arguments().cloned().map(|a| { - if a.get_id() == "id" { - a.required(false).required_unless_present("sideload") - } else { - a - } - .conflicts_with("sideload") - })) + .args( + QueryPackageParams::command() + .get_arguments() + .cloned() + .map(|a| { + if a.get_id() == "id" { + a.required(false).required_unless_present("sideload") + } else { + a + } + .conflicts_with("sideload") + }), + ) } fn command_for_update() -> clap::Command { Self::command() @@ -346,7 +312,9 @@ impl FromArgMatches for CliInstallParams { if let Some(sideload) = matches.get_one::("sideload") { Ok(Self::Sideload(sideload.clone())) } else { - Ok(Self::Marketplace(InstallParams::from_arg_matches(matches)?)) + Ok(Self::Marketplace(QueryPackageParams::from_arg_matches( + matches, + )?)) } } fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { @@ -355,6 +323,35 @@ impl FromArgMatches for CliInstallParams { } } +#[derive(Deserialize, Serialize, Parser, TS)] +#[ts(export)] +pub struct InstalledVersionParams { + id: PackageId, +} + +pub async fn installed_version( + ctx: RpcContext, + InstalledVersionParams { id }: InstalledVersionParams, +) -> Result, Error> { + if let Some(pde) = ctx + .db + .peek() + .await + .into_public() + .into_package_data() + .into_idx(&id) + { + Ok(Some( + pde.into_state_info() + .as_manifest(ManifestPreference::Old) + .as_version() + .de()?, + )) + } else { + Ok(None) + } +} + #[instrument(skip_all)] pub async fn cli_install( HandlerArgs { @@ -368,7 +365,7 @@ pub async fn cli_install( let method = parent_method.into_iter().chain(method).collect_vec(); match params { CliInstallParams::Sideload(path) => { - let file = crate::s9pk::load(&ctx, path).await?; + let file = open_file(path).await?; // rpc call remote sideload let SideloadResponse { upload, progress } = from_value::( @@ -435,9 +432,70 @@ pub async fn cli_install( progress?; upload?; } - CliInstallParams::Marketplace(params) => { - ctx.call_remote::(&method.join("."), to_value(¶ms)?) - .await?; + CliInstallParams::Marketplace(QueryPackageParams { id, version }) => { + let source_version: Option = from_value( + ctx.call_remote::("package.installed-version", json!({ "id": &id })) + .await?, + )?; + let mut packages: GetPackageResponse = from_value( + ctx.call_remote::( + "package.get", + json!({ "id": &id, "version": version, "sourceVersion": source_version }), + ) + .await?, + )?; + let version = if packages.best.len() == 1 { + packages.best.pop_first().map(|(k, _)| k).unwrap() + } else { + println!("Multiple flavors of {id} found. Please select one of the following versions to install:"); + let version; + loop { + let (mut read, mut output) = rustyline_async::Readline::new("> ".into()) + .with_kind(ErrorKind::Filesystem)?; + for (idx, version) in packages.best.keys().enumerate() { + output + .write_all(format!(" {}) {}\n", idx + 1, version).as_bytes()) + .await?; + read.add_history_entry(version.to_string()); + } + if let ReadlineEvent::Line(line) = read.readline().await? { + let trimmed = line.trim(); + match trimmed.parse() { + Ok(v) => { + if let Some((k, _)) = packages.best.remove_entry(&v) { + version = k; + break; + } + } + Err(_) => match trimmed.parse::() { + Ok(i) if (1..=packages.best.len()).contains(&i) => { + version = packages.best.keys().nth(i - 1).unwrap().clone(); + break; + } + _ => (), + }, + } + eprintln!("invalid selection: {trimmed}"); + println!("Please select one of the following versions to install:"); + } else { + return Err(Error::new( + eyre!("Could not determine precise version to install"), + ErrorKind::InvalidRequest, + ) + .into()); + } + } + version + }; + ctx.call_remote::( + &method.join("."), + to_value(&InstallParams { + id, + registry: ctx.registry_url.clone().or_not_found("--registry")?, + version, + })?, + ) + .await?; } } Ok(()) diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index 5a9561daf..4882d998e 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -1,4 +1,4 @@ -pub const DEFAULT_MARKETPLACE: &str = "https://registry.start9.com"; +pub const DEFAULT_REGISTRY: &str = "https://registry.start9.com"; // pub const COMMUNITY_MARKETPLACE: &str = "https://community-registry.start9.com"; pub const HOST_IP: [u8; 4] = [172, 18, 0, 1]; pub use std::env::consts::ARCH; @@ -263,6 +263,12 @@ pub fn package() -> ParentHandler { .with_display_serializable() .with_call_remote::(), ) + .subcommand( + "installed-version", + from_fn_async(install::installed_version) + .with_display_serializable() + .with_call_remote::(), + ) .subcommand("config", config::config::()) .subcommand( "start", diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index 6af707a6d..99f019d5a 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -12,7 +12,6 @@ use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{GenericRpcMethod, RpcRequest, RpcResponse}; use rustyline_async::{ReadlineEvent, SharedWriter}; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; use tokio::sync::Mutex; @@ -30,6 +29,7 @@ use crate::disk::mount::util::unmount; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::util::clap::FromStrParser; +use crate::util::io::open_file; use crate::util::rpc_client::UnixRpcClient; use crate::util::{new_guid, Invoke}; @@ -342,7 +342,7 @@ impl Drop for LxcContainer { if let Err(e) = async { let err_path = rootfs.path().join("var/log/containerRuntime.err"); if tokio::fs::metadata(&err_path).await.is_ok() { - let mut lines = BufReader::new(File::open(&err_path).await?).lines(); + let mut lines = BufReader::new(open_file(&err_path).await?).lines(); while let Some(line) = lines.next_line().await? { let container = &**guid; tracing::error!(container, "{}", line); diff --git a/core/startos/src/net/service_interface.rs b/core/startos/src/net/service_interface.rs index e905be545..dbe228ef2 100644 --- a/core/startos/src/net/service_interface.rs +++ b/core/startos/src/net/service_interface.rs @@ -5,8 +5,6 @@ use models::{HostId, ServiceInterfaceId}; use serde::{Deserialize, Serialize}; use ts_rs::TS; -use crate::net::host::address::HostAddress; - #[derive(Clone, Debug, Deserialize, Serialize, TS)] #[ts(export)] #[serde(rename_all = "camelCase")] diff --git a/core/startos/src/net/static_server.rs b/core/startos/src/net/static_server.rs index 7e8034d99..79adad504 100644 --- a/core/startos/src/net/static_server.rs +++ b/core/startos/src/net/static_server.rs @@ -19,7 +19,6 @@ use new_mime_guess::MimeGuess; use openssl::hash::MessageDigest; use openssl::x509::X509; use rpc_toolkit::{Context, HttpServer, Server}; -use tokio::fs::File; use tokio::io::BufReader; use tokio_util::io::ReaderStream; @@ -29,6 +28,7 @@ use crate::middleware::auth::{Auth, HasValidSession}; use crate::middleware::cors::Cors; use crate::middleware::db::SyncDb; use crate::rpc_continuations::{Guid, RpcContinuations}; +use crate::util::io::open_file; use crate::{ diagnostic_api, init_api, install_api, main_api, setup_api, Error, ErrorKind, ResultExt, }; @@ -44,8 +44,6 @@ const EMBEDDED_UIS: Dir<'_> = #[cfg(not(all(feature = "daemon", not(feature = "test"))))] const EMBEDDED_UIS: Dir<'_> = Dir::new("", &[]); -const PROXY_STRIP_HEADERS: &[&str] = &["cookie", "host", "origin", "referer", "user-agent"]; - #[derive(Clone)] pub enum UiMode { Setup, @@ -340,9 +338,8 @@ impl FileData { .any(|e| e == "gzip") .then_some("gzip"); - let file = File::open(path) - .await - .with_ctx(|_| (ErrorKind::Filesystem, path.display().to_string()))?; + let file = open_file(path) + .await?; let metadata = file .metadata() .await diff --git a/core/startos/src/notifications.rs b/core/startos/src/notifications.rs index fde3d5e80..c99ffb356 100644 --- a/core/startos/src/notifications.rs +++ b/core/startos/src/notifications.rs @@ -74,6 +74,7 @@ pub async fn list( .as_notifications() .as_entries()? .into_iter() + .rev() .take(limit); let notifs = records .into_iter() @@ -97,6 +98,7 @@ pub async fn list( .as_entries()? .into_iter() .filter(|(id, _)| *id < before) + .rev() .take(limit); records .into_iter() diff --git a/core/startos/src/os_install/mod.rs b/core/startos/src/os_install/mod.rs index 4e5c7ed15..28fd2a3be 100644 --- a/core/startos/src/os_install/mod.rs +++ b/core/startos/src/os_install/mod.rs @@ -21,7 +21,7 @@ use crate::disk::OsPartitionInfo; use crate::net::utils::find_eth_iface; use crate::prelude::*; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; -use crate::util::io::TmpDir; +use crate::util::io::{open_file, TmpDir}; use crate::util::serde::IoFormat; use crate::util::Invoke; use crate::ARCH; @@ -241,12 +241,10 @@ pub async fn execute( tokio::fs::create_dir_all(&images_path).await?; let image_path = images_path .join(hex::encode( - &MultiCursorFile::from( - tokio::fs::File::open("/run/live/medium/live/filesystem.squashfs").await?, - ) - .blake3_mmap() - .await? - .as_bytes()[..16], + &MultiCursorFile::from(open_file("/run/live/medium/live/filesystem.squashfs").await?) + .blake3_mmap() + .await? + .as_bytes()[..16], )) .with_extension("rootfs"); tokio::fs::copy("/run/live/medium/live/filesystem.squashfs", &image_path).await?; diff --git a/core/startos/src/registry/asset.rs b/core/startos/src/registry/asset.rs index ea37b2309..ab251d2aa 100644 --- a/core/startos/src/registry/asset.rs +++ b/core/startos/src/registry/asset.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::sync::Arc; use reqwest::Client; use serde::{Deserialize, Serialize}; @@ -7,10 +8,13 @@ use ts_rs::TS; use url::Url; use crate::prelude::*; +use crate::registry::signer::commitment::merkle_archive::MerkleArchiveCommitment; use crate::registry::signer::commitment::{Commitment, Digestable}; use crate::registry::signer::sign::{AnySignature, AnyVerifyingKey}; use crate::registry::signer::AcceptSigners; use crate::s9pk::merkle_archive::source::http::HttpSource; +use crate::s9pk::merkle_archive::source::Section; +use crate::s9pk::S9pk; #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] @@ -52,3 +56,15 @@ impl Commitment<&'a HttpSource>> RegistryAsset { .await } } +impl RegistryAsset { + pub async fn deserialize_s9pk( + &self, + client: Client, + ) -> Result>>, Error> { + S9pk::deserialize( + &Arc::new(HttpSource::new(client, self.url.clone()).await?), + Some(&self.commitment), + ) + .await + } +} diff --git a/core/startos/src/registry/device_info.rs b/core/startos/src/registry/device_info.rs index 51d6ac46b..9a357358a 100644 --- a/core/startos/src/registry/device_info.rs +++ b/core/startos/src/registry/device_info.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use axum::extract::Request; use axum::response::Response; -use emver::{Version, VersionRange}; +use exver::{Version, VersionRange}; use http::HeaderValue; use imbl_value::InternedString; use rpc_toolkit::{Middleware, RpcRequest, RpcResponse}; diff --git a/core/startos/src/registry/mod.rs b/core/startos/src/registry/mod.rs index 9a53d6338..1039264df 100644 --- a/core/startos/src/registry/mod.rs +++ b/core/startos/src/registry/mod.rs @@ -1,5 +1,4 @@ use std::collections::{BTreeMap, BTreeSet}; -use std::net::SocketAddr; use axum::Router; use futures::future::ready; diff --git a/core/startos/src/registry/os/asset/add.rs b/core/startos/src/registry/os/asset/add.rs index 33f5ef90f..6108dd5bc 100644 --- a/core/startos/src/registry/os/asset/add.rs +++ b/core/startos/src/registry/os/asset/add.rs @@ -3,7 +3,6 @@ use std::panic::UnwindSafe; use std::path::PathBuf; use clap::Parser; -use helpers::NonDetachingJoinHandle; use imbl_value::InternedString; use itertools::Itertools; use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler}; @@ -13,7 +12,7 @@ use url::Url; use crate::context::CliContext; use crate::prelude::*; -use crate::progress::{FullProgressTracker, PhasedProgressBar}; +use crate::progress::{FullProgressTracker}; use crate::registry::asset::RegistryAsset; use crate::registry::context::RegistryContext; use crate::registry::os::index::OsVersionInfo; @@ -25,6 +24,7 @@ use crate::s9pk::merkle_archive::hash::VerifyingWriter; use crate::s9pk::merkle_archive::source::http::HttpSource; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::merkle_archive::source::ArchiveSource; +use crate::util::io::open_file; use crate::util::serde::Base64; use crate::util::VersionString; @@ -184,7 +184,7 @@ pub async fn cli_add_asset( } }; - let file = MultiCursorFile::from(tokio::fs::File::open(&path).await?); + let file = MultiCursorFile::from(open_file(&path).await?); let progress = FullProgressTracker::new(); let mut sign_phase = progress.add_phase(InternedString::intern("Signing File"), Some(10)); diff --git a/core/startos/src/registry/os/asset/get.rs b/core/startos/src/registry/os/asset/get.rs index 29ff24da6..b185cf6a4 100644 --- a/core/startos/src/registry/os/asset/get.rs +++ b/core/startos/src/registry/os/asset/get.rs @@ -20,6 +20,7 @@ use crate::registry::os::SIG_CONTEXT; use crate::registry::signer::commitment::blake3::Blake3Commitment; use crate::registry::signer::commitment::Commitment; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; +use crate::util::io::open_file; use crate::util::VersionString; pub fn get_api() -> ParentHandler { @@ -158,9 +159,7 @@ async fn cli_get_os_asset( if let Some(mut reverify_phase) = reverify_phase { reverify_phase.start(); res.commitment - .check(&MultiCursorFile::from( - tokio::fs::File::open(download).await?, - )) + .check(&MultiCursorFile::from(open_file(download).await?)) .await?; reverify_phase.complete(); } diff --git a/core/startos/src/registry/os/asset/sign.rs b/core/startos/src/registry/os/asset/sign.rs index 50c583593..8bf1cfeb5 100644 --- a/core/startos/src/registry/os/asset/sign.rs +++ b/core/startos/src/registry/os/asset/sign.rs @@ -21,6 +21,7 @@ use crate::registry::signer::sign::ed25519::Ed25519; use crate::registry::signer::sign::{AnySignature, AnyVerifyingKey, SignatureScheme}; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::merkle_archive::source::ArchiveSource; +use crate::util::io::open_file; use crate::util::serde::Base64; use crate::util::VersionString; @@ -166,7 +167,7 @@ pub async fn cli_sign_asset( } }; - let file = MultiCursorFile::from(tokio::fs::File::open(&path).await?); + let file = MultiCursorFile::from(open_file(&path).await?); let progress = FullProgressTracker::new(); let mut sign_phase = progress.add_phase(InternedString::intern("Signing File"), Some(10)); diff --git a/core/startos/src/registry/os/index.rs b/core/startos/src/registry/os/index.rs index 3ee75bc6a..0b1ca5b89 100644 --- a/core/startos/src/registry/os/index.rs +++ b/core/startos/src/registry/os/index.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeMap, BTreeSet}; -use emver::VersionRange; +use exver::VersionRange; use imbl_value::InternedString; use serde::{Deserialize, Serialize}; use ts_rs::TS; diff --git a/core/startos/src/registry/os/version/mod.rs b/core/startos/src/registry/os/version/mod.rs index 234143295..9ebe8a696 100644 --- a/core/startos/src/registry/os/version/mod.rs +++ b/core/startos/src/registry/os/version/mod.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use chrono::Utc; use clap::Parser; -use emver::VersionRange; +use exver::VersionRange; use itertools::Itertools; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; @@ -148,10 +148,11 @@ pub async fn get_version( if let (Some(pool), Some(server_id), Some(arch)) = (&ctx.pool, server_id, arch) { let created_at = Utc::now(); - query!("INSERT INTO user_activity (created_at, server_id, arch) VALUES ($1, $2, $3)", - created_at, - server_id, - arch + query!( + "INSERT INTO user_activity (created_at, server_id, arch) VALUES ($1, $2, $3)", + created_at, + server_id, + arch ) .execute(pool) .await?; diff --git a/core/startos/src/registry/package/get.rs b/core/startos/src/registry/package/get.rs index 835192361..fb63be1bc 100644 --- a/core/startos/src/registry/package/get.rs +++ b/core/startos/src/registry/package/get.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use clap::{Parser, ValueEnum}; -use emver::{Version, VersionRange}; +use exver::{ExtendedVersion, VersionRange}; use imbl_value::InternedString; use itertools::Itertools; use models::PackageId; @@ -45,8 +45,7 @@ pub struct GetPackageParams { pub id: Option, #[ts(type = "string | null")] pub version: Option, - #[ts(type = "string | null")] - pub source_version: Option, + pub source_version: Option, #[ts(skip)] #[arg(skip)] #[serde(rename = "__device_info")] @@ -132,7 +131,7 @@ fn get_matching_models<'a>( device_info, .. }: &GetPackageParams, -) -> Result)>, Error> { +) -> Result)>, Error> { if let Some(id) = id { if let Some(pkg) = db.as_packages().as_idx(id) { vec![(id.clone(), pkg)] @@ -166,7 +165,7 @@ fn get_matching_models<'a>( .as_ref() .map_or(Ok(true), |device_info| info.works_for_device(device_info))? { - Some((k.clone(), Version::from(v), info)) + Some((k.clone(), ExtendedVersion::from(v), info)) } else { None }, diff --git a/core/startos/src/registry/package/index.rs b/core/startos/src/registry/package/index.rs index 0e6969fa5..80055f06d 100644 --- a/core/startos/src/registry/package/index.rs +++ b/core/startos/src/registry/package/index.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeMap, BTreeSet}; -use emver::{Version, VersionRange}; +use exver::{Version, VersionRange}; use imbl_value::InternedString; use models::{DataUrl, PackageId, VersionString}; use serde::{Deserialize, Serialize}; @@ -70,7 +70,8 @@ pub struct PackageVersionInfo { pub support_site: Url, #[ts(type = "string")] pub marketing_site: Url, - pub os_version: VersionString, + #[ts(type = "string")] + pub os_version: Version, pub hardware_requirements: HardwareRequirements, #[ts(type = "string | null")] pub source_version: Option, diff --git a/core/startos/src/registry/signer/sign/mod.rs b/core/startos/src/registry/signer/sign/mod.rs index 50576a198..a29109864 100644 --- a/core/startos/src/registry/signer/sign/mod.rs +++ b/core/startos/src/registry/signer/sign/mod.rs @@ -4,7 +4,6 @@ use std::str::FromStr; use ::ed25519::pkcs8::BitStringRef; use clap::builder::ValueParserFactory; use der::referenced::OwnedToRef; -use der::{Decode, Encode}; use pkcs8::der::AnyRef; use pkcs8::{PrivateKeyInfo, SubjectPublicKeyInfo}; use serde::{Deserialize, Serialize}; diff --git a/core/startos/src/s9pk/merkle_archive/directory_contents.rs b/core/startos/src/s9pk/merkle_archive/directory_contents.rs index c5e5c4a7d..b39789222 100644 --- a/core/startos/src/s9pk/merkle_archive/directory_contents.rs +++ b/core/startos/src/s9pk/merkle_archive/directory_contents.rs @@ -274,6 +274,21 @@ impl DirectoryContents { ((_, a), (_, b), _) if !a.as_contents().is_dir() && b.as_contents().is_dir() => { std::cmp::Ordering::Greater } + ((_, a), (_, b), _) + if a.as_contents().is_missing() && !b.as_contents().is_missing() => + { + std::cmp::Ordering::Greater + } + ((_, a), (_, b), _) + if !a.as_contents().is_missing() && b.as_contents().is_missing() => + { + std::cmp::Ordering::Less + } + ((n_a, a), (n_b, b), _) + if a.as_contents().is_missing() && b.as_contents().is_missing() => + { + n_a.cmp(n_b) + } ((a, _), (b, _), Some(sort_by)) => sort_by(&***a, &***b), _ => std::cmp::Ordering::Equal, }) { diff --git a/core/startos/src/s9pk/merkle_archive/mod.rs b/core/startos/src/s9pk/merkle_archive/mod.rs index 00ead65c5..977e5ebb2 100644 --- a/core/startos/src/s9pk/merkle_archive/mod.rs +++ b/core/startos/src/s9pk/merkle_archive/mod.rs @@ -121,14 +121,14 @@ impl MerkleArchive> { } if max_size > *root_maxsize { return Err(Error::new( - eyre!("merkle root directory max size too large"), + eyre!("root directory max size too large"), ErrorKind::InvalidSignature, )); } } else { if max_size > CAP_1_MiB as u64 { return Err(Error::new( - eyre!("merkle root directory max size over 1MiB, cancelling download in case of DOS attack"), + eyre!("root directory max size over 1MiB, cancelling download in case of DOS attack"), ErrorKind::InvalidSignature, )); } @@ -377,6 +377,9 @@ impl EntryContents { pub fn is_dir(&self) -> bool { matches!(self, &EntryContents::Directory(_)) } + pub fn is_missing(&self) -> bool { + matches!(self, &EntryContents::Missing) + } } impl EntryContents> { #[instrument(skip_all)] diff --git a/core/startos/src/s9pk/merkle_archive/source/http.rs b/core/startos/src/s9pk/merkle_archive/source/http.rs index fda9d32ed..e58208277 100644 --- a/core/startos/src/s9pk/merkle_archive/source/http.rs +++ b/core/startos/src/s9pk/merkle_archive/source/http.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex}; use std::task::Poll; use bytes::Bytes; -use futures::{Stream, StreamExt, TryStreamExt}; +use futures::{Stream, TryStreamExt}; use reqwest::header::{ACCEPT_RANGES, CONTENT_LENGTH, RANGE}; use reqwest::{Client, Url}; use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf, Take}; @@ -54,11 +54,12 @@ impl HttpSource { } } impl ArchiveSource for HttpSource { - type Reader = HttpReader; + type FetchReader = HttpReader; + type FetchAllReader = StreamReader>, Bytes>; async fn size(&self) -> Option { self.size } - async fn fetch_all(&self) -> Result { + async fn fetch_all(&self) -> Result { Ok(StreamReader::new( self.client .get(self.url.clone()) @@ -72,7 +73,7 @@ impl ArchiveSource for HttpSource { .apply(boxed), )) } - async fn fetch(&self, position: u64, size: u64) -> Result { + async fn fetch(&self, position: u64, size: u64) -> Result { match &self.range_support { Ok(_) => Ok(HttpReader::Range( StreamReader::new(if size > 0 { diff --git a/core/startos/src/s9pk/merkle_archive/source/mod.rs b/core/startos/src/s9pk/merkle_archive/source/mod.rs index 0a00b18dd..6b7459787 100644 --- a/core/startos/src/s9pk/merkle_archive/source/mod.rs +++ b/core/startos/src/s9pk/merkle_archive/source/mod.rs @@ -10,6 +10,7 @@ use tokio::io::{AsyncRead, AsyncWrite}; use crate::prelude::*; use crate::s9pk::merkle_archive::hash::VerifyingWriter; +use crate::util::io::{open_file, TmpDir}; pub mod http; pub mod multi_cursor_file; @@ -159,7 +160,7 @@ impl FileSource for PathBuf { Ok(tokio::fs::metadata(self).await?.len()) } async fn reader(&self) -> Result { - Ok(File::open(self).await?) + Ok(open_file(self).await?) } } @@ -180,18 +181,17 @@ impl FileSource for Arc<[u8]> { } pub trait ArchiveSource: Send + Sync + Sized + 'static { - type Reader: AsyncRead + Unpin + Send; + type FetchReader: AsyncRead + Unpin + Send; + type FetchAllReader: AsyncRead + Unpin + Send; fn size(&self) -> impl Future> + Send { async { None } } - fn fetch_all( - &self, - ) -> impl Future> + Send; + fn fetch_all(&self) -> impl Future> + Send; fn fetch( &self, position: u64, size: u64, - ) -> impl Future> + Send; + ) -> impl Future> + Send; fn copy_all_to( &self, w: &mut W, @@ -222,14 +222,15 @@ pub trait ArchiveSource: Send + Sync + Sized + 'static { } impl ArchiveSource for Arc { - type Reader = T::Reader; + type FetchReader = T::FetchReader; + type FetchAllReader = T::FetchAllReader; async fn size(&self) -> Option { self.deref().size().await } - async fn fetch_all(&self) -> Result { + async fn fetch_all(&self) -> Result { self.deref().fetch_all().await } - async fn fetch(&self, position: u64, size: u64) -> Result { + async fn fetch(&self, position: u64, size: u64) -> Result { self.deref().fetch(position, size).await } async fn copy_all_to( @@ -249,11 +250,12 @@ impl ArchiveSource for Arc { } impl ArchiveSource for Arc<[u8]> { - type Reader = tokio::io::Take>; - async fn fetch_all(&self) -> Result { + type FetchReader = tokio::io::Take>; + type FetchAllReader = std::io::Cursor; + async fn fetch_all(&self) -> Result { Ok(std::io::Cursor::new(self.clone())) } - async fn fetch(&self, position: u64, size: u64) -> Result { + async fn fetch(&self, position: u64, size: u64) -> Result { use tokio::io::AsyncReadExt; let mut cur = std::io::Cursor::new(self.clone()); @@ -269,7 +271,7 @@ pub struct Section { size: u64, } impl FileSource for Section { - type Reader = S::Reader; + type Reader = S::FetchReader; async fn size(&self) -> Result { Ok(self.size) } @@ -285,3 +287,81 @@ pub type DynRead = Box; pub fn into_dyn_read(r: R) -> DynRead { Box::new(r) } + +#[derive(Clone)] +pub struct TmpSource { + tmp_dir: Arc, + source: S, +} +impl TmpSource { + pub fn new(tmp_dir: Arc, source: S) -> Self { + Self { tmp_dir, source } + } + pub async fn gc(self) -> Result<(), Error> { + self.tmp_dir.gc().await + } +} +impl std::ops::Deref for TmpSource { + type Target = S; + fn deref(&self) -> &Self::Target { + &self.source + } +} +impl ArchiveSource for TmpSource { + type FetchReader = ::FetchReader; + type FetchAllReader = ::FetchAllReader; + async fn size(&self) -> Option { + self.source.size().await + } + async fn fetch_all(&self) -> Result { + self.source.fetch_all().await + } + async fn fetch(&self, position: u64, size: u64) -> Result { + self.source.fetch(position, size).await + } + async fn copy_all_to( + &self, + w: &mut W, + ) -> Result<(), Error> { + self.source.copy_all_to(w).await + } + async fn copy_to( + &self, + position: u64, + size: u64, + w: &mut W, + ) -> Result<(), Error> { + self.source.copy_to(position, size, w).await + } +} +impl From> for DynFileSource { + fn from(value: TmpSource) -> Self { + DynFileSource::new(value) + } +} + +impl FileSource for TmpSource { + type Reader = ::Reader; + async fn size(&self) -> Result { + self.source.size().await + } + async fn reader(&self) -> Result { + self.source.reader().await + } + async fn copy( + &self, + mut w: &mut W, + ) -> Result<(), Error> { + self.source.copy(&mut w).await + } + async fn copy_verify( + &self, + mut w: &mut W, + verify: Option<(Hash, u64)>, + ) -> Result<(), Error> { + self.source.copy_verify(&mut w, verify).await + } + async fn to_vec(&self, verify: Option<(Hash, u64)>) -> Result, Error> { + self.source.to_vec(verify).await + } +} diff --git a/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs b/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs index 4b7d5736d..658f3f923 100644 --- a/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs +++ b/core/startos/src/s9pk/merkle_archive/source/multi_cursor_file.rs @@ -6,12 +6,13 @@ use std::sync::Arc; use std::task::Poll; use tokio::fs::File; -use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf, Take}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, ReadBuf, Take}; use tokio::sync::{Mutex, OwnedMutexGuard}; use crate::disk::mount::filesystem::loop_dev::LoopDev; use crate::prelude::*; use crate::s9pk::merkle_archive::source::{ArchiveSource, Section}; +use crate::util::io::open_file; fn path_from_fd(fd: RawFd) -> Result { #[cfg(target_os = "linux")] @@ -42,7 +43,7 @@ impl MultiCursorFile { path_from_fd(self.fd) } pub async fn open(fd: &impl AsRawFd) -> Result { - let f = File::open(path_from_fd(fd.as_raw_fd())?).await?; + let f = open_file(path_from_fd(fd.as_raw_fd())?).await?; Ok(Self::from(f)) } pub async fn cursor(&self) -> Result { @@ -50,7 +51,7 @@ impl MultiCursorFile { if let Ok(file) = self.file.clone().try_lock_owned() { file } else { - Arc::new(Mutex::new(File::open(self.path()?).await?)) + Arc::new(Mutex::new(open_file(self.path()?).await?)) .try_lock_owned() .expect("freshly created") }, @@ -88,24 +89,48 @@ impl AsyncRead for FileCursor { Pin::new(&mut (&mut **this.0.get_mut())).poll_read(cx, buf) } } +impl AsyncSeek for FileCursor { + fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> { + let this = self.project(); + Pin::new(&mut (&mut **this.0.get_mut())).start_seek(position) + } + fn poll_complete( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + let this = self.project(); + Pin::new(&mut (&mut **this.0.get_mut())).poll_complete(cx) + } +} +impl std::ops::Deref for FileCursor { + type Target = File; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} +impl std::ops::DerefMut for FileCursor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} impl ArchiveSource for MultiCursorFile { - type Reader = Take; + type FetchReader = Take; + type FetchAllReader = FileCursor; async fn size(&self) -> Option { tokio::fs::metadata(self.path().ok()?) .await .ok() .map(|m| m.len()) } - #[allow(refining_impl_trait)] - async fn fetch_all(&self) -> Result { + async fn fetch_all(&self) -> Result { use tokio::io::AsyncSeekExt; let mut file = self.cursor().await?; file.0.seek(SeekFrom::Start(0)).await?; Ok(file) } - async fn fetch(&self, position: u64, size: u64) -> Result { + async fn fetch(&self, position: u64, size: u64) -> Result { use tokio::io::AsyncSeekExt; let mut file = self.cursor().await?; diff --git a/core/startos/src/s9pk/merkle_archive/varint.rs b/core/startos/src/s9pk/merkle_archive/varint.rs index 479b488e6..f4f18d140 100644 --- a/core/startos/src/s9pk/merkle_archive/varint.rs +++ b/core/startos/src/s9pk/merkle_archive/varint.rs @@ -3,7 +3,7 @@ use tokio::io::{AsyncRead, AsyncWrite}; use crate::prelude::*; -/// Most-significant byte, == 0x80 +/// Most-significant bit, == 0x80 pub const MSB: u8 = 0b1000_0000; const MAX_STR_LEN: u64 = 1024 * 1024; // 1 MiB @@ -39,22 +39,20 @@ pub async fn serialize_varstring( Ok(()) } +const MAX_SIZE: usize = (std::mem::size_of::() * 8 + 7) / 7; + #[derive(Default)] struct VarIntProcessor { - buf: [u8; 10], - maxsize: usize, + buf: [u8; MAX_SIZE], i: usize, } impl VarIntProcessor { fn new() -> VarIntProcessor { - VarIntProcessor { - maxsize: (std::mem::size_of::() * 8 + 7) / 7, - ..VarIntProcessor::default() - } + Self::default() } fn push(&mut self, b: u8) -> Result<(), Error> { - if self.i >= self.maxsize { + if self.i >= MAX_SIZE { return Err(Error::new( eyre!("Unterminated varint"), ErrorKind::ParseS9pk, diff --git a/core/startos/src/s9pk/mod.rs b/core/startos/src/s9pk/mod.rs index fcf9379a0..a06218d40 100644 --- a/core/startos/src/s9pk/mod.rs +++ b/core/startos/src/s9pk/mod.rs @@ -4,37 +4,57 @@ pub mod rpc; pub mod v1; pub mod v2; -use std::io::SeekFrom; -use std::path::Path; +use std::sync::Arc; -use tokio::fs::File; -use tokio::io::{AsyncReadExt, AsyncSeekExt}; +use tokio::io::{AsyncReadExt, AsyncSeek}; pub use v2::{manifest, S9pk}; -use crate::context::CliContext; use crate::prelude::*; +use crate::progress::FullProgressTracker; +use crate::s9pk::merkle_archive::source::{ArchiveSource, DynFileSource}; use crate::s9pk::v1::reader::S9pkReader; use crate::s9pk::v2::compat::MAGIC_AND_VERSION; +use crate::util::io::TmpDir; -pub async fn load(ctx: &CliContext, path: impl AsRef) -> Result { +pub async fn load( + source: S, + key: K, + progress: Option<&FullProgressTracker>, +) -> Result, Error> +where + S: ArchiveSource, + S::FetchAllReader: AsyncSeek + Sync, + K: FnOnce() -> Result, +{ // TODO: return s9pk const MAGIC_LEN: usize = MAGIC_AND_VERSION.len(); let mut magic = [0_u8; MAGIC_LEN]; - let mut file = tokio::fs::File::open(&path).await?; - file.read_exact(&mut magic).await?; - file.seek(SeekFrom::Start(0)).await?; + source.fetch(0, 3).await?.read_exact(&mut magic).await?; if magic == v2::compat::MAGIC_AND_VERSION { + let phase = if let Some(progress) = progress { + let mut phase = progress.add_phase( + "Converting Package to V2".into(), + Some(source.size().await.unwrap_or(60)), + ); + phase.start(); + Some(phase) + } else { + None + }; tracing::info!("Converting package to v2 s9pk"); - let new_path = path.as_ref().with_extension("compat.s9pk"); - S9pk::from_v1( - S9pkReader::from_reader(file, true).await?, - &new_path, - ctx.developer_key()?.clone(), + let tmp_dir = TmpDir::new().await?; + let s9pk = S9pk::from_v1( + S9pkReader::from_reader(source.fetch_all().await?, true).await?, + Arc::new(tmp_dir), + key()?, ) .await?; - tokio::fs::rename(&new_path, &path).await?; - file = tokio::fs::File::open(&path).await?; tracing::info!("Converted s9pk successfully"); + if let Some(mut phase) = phase { + phase.complete(); + } + Ok(s9pk.into_dyn()) + } else { + Ok(S9pk::deserialize(&Arc::new(source), None).await?.into_dyn()) } - Ok(file) } diff --git a/core/startos/src/s9pk/rpc.rs b/core/startos/src/s9pk/rpc.rs index fac9e6724..83b78dad7 100644 --- a/core/startos/src/s9pk/rpc.rs +++ b/core/startos/src/s9pk/rpc.rs @@ -1,19 +1,19 @@ use std::path::PathBuf; +use std::sync::Arc; use clap::Parser; use models::ImageId; use rpc_toolkit::{from_fn_async, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use ts_rs::TS; use crate::context::CliContext; use crate::prelude::*; use crate::s9pk::manifest::Manifest; +use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::v2::pack::ImageConfig; use crate::s9pk::v2::SIG_CONTEXT; -use crate::s9pk::S9pk; -use crate::util::io::TmpDir; +use crate::util::io::{create_file, open_file, TmpDir}; use crate::util::serde::{apply_expr, HandlerExtSerde}; pub const SKIP_ENV: &[&str] = &["TERM", "container", "HOME", "HOSTNAME"]; @@ -79,19 +79,25 @@ async fn add_image( AddImageParams { id, config }: AddImageParams, S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result<(), Error> { - let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?) - .await? - .into_dyn(); + let mut s9pk = super::load( + MultiCursorFile::from(open_file(&s9pk_path).await?), + || ctx.developer_key().cloned(), + None, + ) + .await?; s9pk.as_manifest_mut().images.insert(id, config); - let tmpdir = TmpDir::new().await?; - s9pk.load_images(&tmpdir).await?; + let tmp_dir = Arc::new(TmpDir::new().await?); + s9pk.load_images(tmp_dir.clone()).await?; s9pk.validate_and_filter(None)?; let tmp_path = s9pk_path.with_extension("s9pk.tmp"); - let mut tmp_file = File::create(&tmp_path).await?; + let mut tmp_file = create_file(&tmp_path).await?; s9pk.serialize(&mut tmp_file, true).await?; + drop(s9pk); tmp_file.sync_all().await?; tokio::fs::rename(&tmp_path, &s9pk_path).await?; + tmp_dir.gc().await?; + Ok(()) } @@ -104,13 +110,18 @@ async fn edit_manifest( EditManifestParams { expression }: EditManifestParams, S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result { - let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?).await?; + let mut s9pk = super::load( + MultiCursorFile::from(open_file(&s9pk_path).await?), + || ctx.developer_key().cloned(), + None, + ) + .await?; let old = serde_json::to_value(s9pk.as_manifest()).with_kind(ErrorKind::Serialization)?; *s9pk.as_manifest_mut() = serde_json::from_value(apply_expr(old.into(), &expression)?.into()) .with_kind(ErrorKind::Serialization)?; let manifest = s9pk.as_manifest().clone(); let tmp_path = s9pk_path.with_extension("s9pk.tmp"); - let mut tmp_file = File::create(&tmp_path).await?; + let mut tmp_file = create_file(&tmp_path).await?; s9pk.as_archive_mut() .set_signer(ctx.developer_key()?.clone(), SIG_CONTEXT); s9pk.serialize(&mut tmp_file, true).await?; @@ -123,9 +134,14 @@ async fn edit_manifest( async fn file_tree( ctx: CliContext, _: Empty, - S9pkPath { s9pk }: S9pkPath, + S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result, Error> { - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; + let s9pk = super::load( + MultiCursorFile::from(open_file(&s9pk_path).await?), + || ctx.developer_key().cloned(), + None, + ) + .await?; Ok(s9pk.as_archive().contents().file_paths("")) } @@ -138,11 +154,16 @@ struct CatParams { async fn cat( ctx: CliContext, CatParams { file_path }: CatParams, - S9pkPath { s9pk }: S9pkPath, + S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result<(), Error> { use crate::s9pk::merkle_archive::source::FileSource; - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; + let s9pk = super::load( + MultiCursorFile::from(open_file(&s9pk_path).await?), + || ctx.developer_key().cloned(), + None, + ) + .await?; tokio::io::copy( &mut s9pk .as_archive() @@ -162,8 +183,13 @@ async fn cat( async fn inspect_manifest( ctx: CliContext, _: Empty, - S9pkPath { s9pk }: S9pkPath, + S9pkPath { s9pk: s9pk_path }: S9pkPath, ) -> Result { - let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?; + let s9pk = super::load( + MultiCursorFile::from(open_file(&s9pk_path).await?), + || ctx.developer_key().cloned(), + None, + ) + .await?; Ok(s9pk.as_manifest().clone()) } diff --git a/core/startos/src/s9pk/v1/manifest.rs b/core/startos/src/s9pk/v1/manifest.rs index 845d8f773..4a9956f9f 100644 --- a/core/startos/src/s9pk/v1/manifest.rs +++ b/core/startos/src/s9pk/v1/manifest.rs @@ -1,8 +1,7 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; -use emver::VersionRange; -use imbl_value::InOMap; +use exver::{Version, VersionRange}; use indexmap::IndexMap; pub use models::PackageId; use models::{ActionId, HealthCheckId, ImageId, VolumeId}; @@ -13,23 +12,16 @@ use crate::prelude::*; use crate::s9pk::git_hash::GitHash; use crate::s9pk::manifest::{Alerts, Description, HardwareRequirements}; use crate::util::serde::{Duration, IoFormat}; -use crate::util::VersionString; -use crate::version::{Current, VersionT}; - -fn current_version() -> VersionString { - Current::new().semver().into() -} #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] pub struct Manifest { - #[serde(default = "current_version")] - pub eos_version: VersionString, + pub eos_version: Version, pub id: PackageId, #[serde(default)] pub git_hash: Option, pub title: String, - pub version: VersionString, + pub version: exver::emver::Version, pub description: Description, #[serde(default)] pub assets: Assets, diff --git a/core/startos/src/s9pk/v1/reader.rs b/core/startos/src/s9pk/v1/reader.rs index 7437fdaa9..f2bbf578e 100644 --- a/core/startos/src/s9pk/v1/reader.rs +++ b/core/startos/src/s9pk/v1/reader.rs @@ -20,6 +20,7 @@ use super::header::{FileSection, Header, TableOfContents}; use super::SIG_CONTEXT; use crate::prelude::*; use crate::s9pk::v1::docker::DockerReader; +use crate::util::io::open_file; use crate::util::VersionString; #[pin_project::pin_project] @@ -150,9 +151,7 @@ pub struct S9pkReader>(path: P, check_sig: bool) -> Result { let p = path.as_ref(); - let rdr = File::open(p) - .await - .with_ctx(|_| (crate::error::ErrorKind::Filesystem, p.display().to_string()))?; + let rdr = open_file(p).await?; Self::from_reader(BufReader::new(rdr), check_sig).await } diff --git a/core/startos/src/s9pk/v2/compat.rs b/core/startos/src/s9pk/v2/compat.rs index ec5b586ea..914d2e5aa 100644 --- a/core/startos/src/s9pk/v2/compat.rs +++ b/core/startos/src/s9pk/v2/compat.rs @@ -2,9 +2,8 @@ use std::collections::BTreeMap; use std::path::Path; use std::sync::Arc; -use itertools::Itertools; +use exver::ExtendedVersion; use models::ImageId; -use tokio::fs::File; use tokio::io::{AsyncRead, AsyncSeek, AsyncWriteExt}; use tokio::process::Command; @@ -12,29 +11,35 @@ use crate::dependencies::{DepInfo, Dependencies}; use crate::prelude::*; use crate::s9pk::manifest::Manifest; use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; -use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; -use crate::s9pk::merkle_archive::source::Section; +use crate::s9pk::merkle_archive::source::TmpSource; use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; -use crate::s9pk::rpc::SKIP_ENV; use crate::s9pk::v1::manifest::{Manifest as ManifestV1, PackageProcedure}; use crate::s9pk::v1::reader::S9pkReader; -use crate::s9pk::v2::pack::{PackSource, CONTAINER_TOOL}; +use crate::s9pk::v2::pack::{ImageSource, PackSource, CONTAINER_TOOL}; use crate::s9pk::v2::{S9pk, SIG_CONTEXT}; -use crate::util::io::TmpDir; +use crate::util::io::{create_file, TmpDir}; use crate::util::Invoke; pub const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x01]; -impl S9pk> { +impl S9pk> { #[instrument(skip_all)] pub async fn from_v1( mut reader: S9pkReader, - destination: impl AsRef, + tmp_dir: Arc, signer: ed25519_dalek::SigningKey, ) -> Result { - let scratch_dir = TmpDir::new().await?; + Command::new(CONTAINER_TOOL) + .arg("run") + .arg("--rm") + .arg("--privileged") + .arg("tonistiigi/binfmt") + .arg("--install") + .arg("all") + .invoke(ErrorKind::Docker) + .await?; - let mut archive = DirectoryContents::::new(); + let mut archive = DirectoryContents::>::new(); // manifest.json let manifest_raw = reader.manifest().await?; @@ -56,33 +61,35 @@ impl S9pk> { let license: Arc<[u8]> = reader.license().await?.to_vec().await?.into(); archive.insert_path( "LICENSE.md", - Entry::file(PackSource::Buffered(license.into())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered(license.into()), + )), )?; // instructions.md let instructions: Arc<[u8]> = reader.instructions().await?.to_vec().await?.into(); archive.insert_path( "instructions.md", - Entry::file(PackSource::Buffered(instructions.into())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered(instructions.into()), + )), )?; // icon.md let icon: Arc<[u8]> = reader.icon().await?.to_vec().await?.into(); archive.insert_path( format!("icon.{}", manifest.assets.icon_type()), - Entry::file(PackSource::Buffered(icon.into())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered(icon.into()), + )), )?; // images for arch in reader.docker_arches().await? { - let images_dir = scratch_dir.join("images").join(&arch); - let docker_platform = if arch == "x86_64" { - "--platform=linux/amd64".to_owned() - } else if arch == "aarch64" { - "--platform=linux/arm64".to_owned() - } else { - format!("--platform=linux/{arch}") - }; + let images_dir = tmp_dir.join("images").join(&arch); tokio::fs::create_dir_all(&images_dir).await?; Command::new(CONTAINER_TOOL) .arg("load") @@ -93,97 +100,24 @@ impl S9pk> { let mut image_config = new_manifest.images.remove(image).unwrap_or_default(); image_config.arch.insert(arch.as_str().into()); new_manifest.images.insert(image.clone(), image_config); - let sqfs_path = images_dir.join(image).with_extension("squashfs"); let image_name = if *system { format!("start9/{}:latest", image) } else { format!("start9/{}/{}:{}", manifest.id, image, manifest.version) }; - let id = String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("create") - .arg(&docker_platform) - .arg(&image_name) - .invoke(ErrorKind::Docker) - .await?, - )?; - let env = String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("run") - .arg("--rm") - .arg(&docker_platform) - .arg("--entrypoint") - .arg("env") - .arg(&image_name) - .invoke(ErrorKind::Docker) - .await?, - )? - .lines() - .filter(|l| { - l.trim() - .split_once("=") - .map_or(false, |(v, _)| !SKIP_ENV.contains(&v)) - }) - .join("\n") - + "\n"; - let workdir = Path::new( - String::from_utf8( - Command::new(CONTAINER_TOOL) - .arg("run") - .arg("--rm") - .arg(&docker_platform) - .arg("--entrypoint") - .arg("pwd") - .arg(&image_name) - .invoke(ErrorKind::Docker) - .await?, - )? - .trim(), - ) - .to_owned(); - Command::new("bash") - .arg("-c") - .arg(format!( - "{CONTAINER_TOOL} export {id} | mksquashfs - {sqfs} -tar", - id = id.trim(), - sqfs = sqfs_path.display() - )) - .invoke(ErrorKind::Docker) + ImageSource::DockerTag(image_name.clone()) + .load( + tmp_dir.clone(), + &new_manifest.id, + &new_manifest.version, + image, + &arch, + &mut archive, + ) .await?; - Command::new(CONTAINER_TOOL) - .arg("rm") - .arg(id.trim()) - .invoke(ErrorKind::Docker) - .await?; - archive.insert_path( - Path::new("images") - .join(&arch) - .join(&image) - .with_extension("squashfs"), - Entry::file(PackSource::File(sqfs_path)), - )?; - archive.insert_path( - Path::new("images") - .join(&arch) - .join(&image) - .with_extension("env"), - Entry::file(PackSource::Buffered(Vec::from(env).into())), - )?; - archive.insert_path( - Path::new("images") - .join(&arch) - .join(&image) - .with_extension("json"), - Entry::file(PackSource::Buffered( - serde_json::to_vec(&serde_json::json!({ - "workdir": workdir - })) - .with_kind(ErrorKind::Serialization)? - .into(), - )), - )?; Command::new(CONTAINER_TOOL) .arg("rmi") + .arg("-f") .arg(&image_name) .invoke(ErrorKind::Docker) .await?; @@ -191,7 +125,7 @@ impl S9pk> { } // assets - let asset_dir = scratch_dir.join("assets"); + let asset_dir = tmp_dir.join("assets"); tokio::fs::create_dir_all(&asset_dir).await?; tokio_tar::Archive::new(reader.assets().await?) .unpack(&asset_dir) @@ -212,21 +146,21 @@ impl S9pk> { Path::new("assets") .join(&asset_id) .with_extension("squashfs"), - Entry::file(PackSource::File(sqfs_path)), + Entry::file(TmpSource::new(tmp_dir.clone(), PackSource::File(sqfs_path))), )?; } // javascript - let js_dir = scratch_dir.join("javascript"); + let js_dir = tmp_dir.join("javascript"); let sqfs_path = js_dir.with_extension("squashfs"); tokio::fs::create_dir_all(&js_dir).await?; if let Some(mut scripts) = reader.scripts().await? { - let mut js_file = File::create(js_dir.join("embassy.js")).await?; + let mut js_file = create_file(js_dir.join("embassy.js")).await?; tokio::io::copy(&mut scripts, &mut js_file).await?; js_file.sync_all().await?; } { - let mut js_file = File::create(js_dir.join("embassyManifest.json")).await?; + let mut js_file = create_file(js_dir.join("embassyManifest.json")).await?; js_file .write_all(&serde_json::to_vec(&manifest_raw).with_kind(ErrorKind::Serialization)?) .await?; @@ -239,30 +173,24 @@ impl S9pk> { .await?; archive.insert_path( Path::new("javascript.squashfs"), - Entry::file(PackSource::File(sqfs_path)), + Entry::file(TmpSource::new(tmp_dir.clone(), PackSource::File(sqfs_path))), )?; archive.insert_path( "manifest.json", - Entry::file(PackSource::Buffered( - serde_json::to_vec::(&new_manifest) - .with_kind(ErrorKind::Serialization)? - .into(), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered( + serde_json::to_vec::(&new_manifest) + .with_kind(ErrorKind::Serialization)? + .into(), + ), )), )?; - let mut s9pk = S9pk::new(MerkleArchive::new(archive, signer, SIG_CONTEXT), None).await?; - let mut dest_file = File::create(destination.as_ref()).await?; - s9pk.serialize(&mut dest_file, false).await?; - dest_file.sync_all().await?; - - scratch_dir.delete().await?; - - Ok(S9pk::deserialize( - &MultiCursorFile::from(File::open(destination.as_ref()).await?), - None, - ) - .await?) + let mut res = S9pk::new(MerkleArchive::new(archive, signer, SIG_CONTEXT), None).await?; + res.as_archive_mut().update_hashes(true).await?; + Ok(res) } } @@ -272,7 +200,7 @@ impl From for Manifest { Self { id: value.id, title: value.title, - version: value.version, + version: ExtendedVersion::from(value.version).into(), release_notes: value.release_notes, license: value.license.into(), wrapper_repo: value.wrapper_repo, diff --git a/core/startos/src/s9pk/v2/manifest.rs b/core/startos/src/s9pk/v2/manifest.rs index 9ae8524fa..9607bb654 100644 --- a/core/startos/src/s9pk/v2/manifest.rs +++ b/core/startos/src/s9pk/v2/manifest.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::path::Path; use color_eyre::eyre::eyre; +use exver::Version; use helpers::const_true; use imbl_value::InternedString; pub use models::PackageId; @@ -20,8 +21,8 @@ use crate::util::serde::Regex; use crate::util::VersionString; use crate::version::{Current, VersionT}; -fn current_version() -> VersionString { - Current::new().semver().into() +fn current_version() -> Version { + Current::new().semver() } #[derive(Clone, Debug, Deserialize, Serialize, HasModel, TS)] @@ -59,7 +60,8 @@ pub struct Manifest { #[ts(type = "string | null")] pub git_hash: Option, #[serde(default = "current_version")] - pub os_version: VersionString, + #[ts(type = "string")] + pub os_version: Version, #[serde(default = "const_true")] pub has_config: bool, } diff --git a/core/startos/src/s9pk/v2/mod.rs b/core/startos/src/s9pk/v2/mod.rs index a1183efa8..2477e63a0 100644 --- a/core/startos/src/s9pk/v2/mod.rs +++ b/core/startos/src/s9pk/v2/mod.rs @@ -12,10 +12,12 @@ use crate::s9pk::manifest::Manifest; use crate::s9pk::merkle_archive::file_contents::FileContents; use crate::s9pk::merkle_archive::sink::Sink; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; -use crate::s9pk::merkle_archive::source::{ArchiveSource, DynFileSource, FileSource, Section}; +use crate::s9pk::merkle_archive::source::{ + ArchiveSource, DynFileSource, FileSource, Section, TmpSource, +}; use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; use crate::s9pk::v2::pack::{ImageSource, PackSource}; -use crate::util::io::TmpDir; +use crate::util::io::{open_file, TmpDir}; const MAGIC_AND_VERSION: &[u8] = &[0x3b, 0x3b, 0x02]; @@ -165,8 +167,8 @@ impl S9pk { } } -impl + FileSource + Clone> S9pk { - pub async fn load_images(&mut self, tmpdir: &TmpDir) -> Result<(), Error> { +impl> + FileSource + Clone> S9pk { + pub async fn load_images(&mut self, tmp_dir: Arc) -> Result<(), Error> { let id = &self.manifest.id; let version = &self.manifest.version; for (image_id, image_config) in &mut self.manifest.images { @@ -175,7 +177,7 @@ impl + FileSource + Clone> S9pk { image_config .source .load( - tmpdir, + tmp_dir.clone(), id, version, image_id, @@ -206,7 +208,7 @@ impl S9pk> { ) .await?; - let mut magic_version = [0u8; 3]; + let mut magic_version = [0u8; MAGIC_AND_VERSION.len()]; header.read_exact(&mut magic_version).await?; ensure_code!( &magic_version == MAGIC_AND_VERSION, @@ -232,7 +234,7 @@ impl S9pk { Self::deserialize(&MultiCursorFile::from(file), None).await } pub async fn open(path: impl AsRef, id: Option<&PackageId>) -> Result { - let res = Self::from_file(tokio::fs::File::open(path).await?).await?; + let res = Self::from_file(open_file(path).await?).await?; if let Some(id) = id { ensure_code!( &res.as_manifest().id == id, diff --git a/core/startos/src/s9pk/v2/pack.rs b/core/startos/src/s9pk/v2/pack.rs index 18fe0472b..fb271e785 100644 --- a/core/startos/src/s9pk/v2/pack.rs +++ b/core/startos/src/s9pk/v2/pack.rs @@ -10,7 +10,6 @@ use futures::{FutureExt, TryStreamExt}; use imbl_value::InternedString; use models::{ImageId, PackageId, VersionString}; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use tokio::io::AsyncRead; use tokio::process::Command; use tokio::sync::OnceCell; @@ -23,12 +22,12 @@ use crate::rpc_continuations::Guid; use crate::s9pk::merkle_archive::directory_contents::DirectoryContents; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::merkle_archive::source::{ - into_dyn_read, ArchiveSource, DynFileSource, FileSource, + into_dyn_read, ArchiveSource, DynFileSource, FileSource, TmpSource, }; use crate::s9pk::merkle_archive::{Entry, MerkleArchive}; use crate::s9pk::v2::SIG_CONTEXT; use crate::s9pk::S9pk; -use crate::util::io::TmpDir; +use crate::util::io::{create_file, open_file, TmpDir}; use crate::util::Invoke; #[cfg(not(feature = "docker"))] @@ -64,7 +63,7 @@ impl SqfsDir { .invoke(ErrorKind::Filesystem) .await?; Ok(MultiCursorFile::from( - File::open(&path) + open_file(&path) .await .with_ctx(|_| (ErrorKind::Filesystem, path.display()))?, )) @@ -100,11 +99,7 @@ impl FileSource for PackSource { async fn reader(&self) -> Result { match self { Self::Buffered(a) => Ok(into_dyn_read(Cursor::new(a.clone()))), - Self::File(f) => Ok(into_dyn_read( - File::open(f) - .await - .with_ctx(|_| (ErrorKind::Filesystem, f.display()))?, - )), + Self::File(f) => Ok(into_dyn_read(open_file(f).await?)), Self::Squashfs(dir) => dir.file().await?.fetch_all().await.map(into_dyn_read), } } @@ -284,9 +279,9 @@ pub enum ImageSource { } impl ImageSource { #[instrument(skip_all)] - pub fn load<'a, S: From + FileSource + Clone>( + pub fn load<'a, S: From> + FileSource + Clone>( &'a self, - tmpdir: &'a TmpDir, + tmp_dir: Arc, id: &'a PackageId, version: &'a VersionString, image_id: &'a ImageId, @@ -331,12 +326,13 @@ impl ImageSource { .arg(&tag) .arg(&docker_platform) .arg("-o") - .arg("type=image") + .arg("type=docker,dest=-") .capture(false) + .pipe(Command::new(CONTAINER_TOOL).arg("load")) .invoke(ErrorKind::Docker) .await?; ImageSource::DockerTag(tag.clone()) - .load(tmpdir, id, version, image_id, arch, into) + .load(tmp_dir, id, version, image_id, arch, into) .await?; Command::new(CONTAINER_TOOL) .arg("rmi") @@ -390,21 +386,24 @@ impl ImageSource { into.insert_path( base_path.with_extension("json"), Entry::file( - PackSource::Buffered( - serde_json::to_vec(&ImageMetadata { - workdir: if config.working_dir == Path::new("") { - "/".into() - } else { - config.working_dir - }, - user: if config.user.is_empty() { - "root".into() - } else { - config.user.into() - }, - }) - .with_kind(ErrorKind::Serialization)? - .into(), + TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered( + serde_json::to_vec(&ImageMetadata { + workdir: if config.working_dir == Path::new("") { + "/".into() + } else { + config.working_dir + }, + user: if config.user.is_empty() { + "root".into() + } else { + config.user.into() + }, + }) + .with_kind(ErrorKind::Serialization)? + .into(), + ), ) .into(), ), @@ -412,10 +411,16 @@ impl ImageSource { into.insert_path( base_path.with_extension("env"), Entry::file( - PackSource::Buffered(config.env.join("\n").into_bytes().into()).into(), + TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered(config.env.join("\n").into_bytes().into()), + ) + .into(), ), )?; - let dest = tmpdir.join(Guid::new().as_ref()).with_extension("squashfs"); + let dest = tmp_dir + .join(Guid::new().as_ref()) + .with_extension("squashfs"); let container = String::from_utf8( Command::new(CONTAINER_TOOL) .arg("create") @@ -438,7 +443,7 @@ impl ImageSource { .await?; into.insert_path( base_path.with_extension("squashfs"), - Entry::file(PackSource::File(dest).into()), + Entry::file(TmpSource::new(tmp_dir.clone(), PackSource::File(dest)).into()), )?; Ok(()) @@ -460,8 +465,8 @@ pub struct ImageMetadata { #[instrument(skip_all)] pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { - let tmpdir = Arc::new(TmpDir::new().await?); - let mut files = DirectoryContents::::new(); + let tmp_dir = Arc::new(TmpDir::new().await?); + let mut files = DirectoryContents::>::new(); let js_dir = params.javascript(); let manifest: Arc<[u8]> = Command::new("node") .arg("-e") @@ -474,7 +479,10 @@ pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { .into(); files.insert( "manifest.json".into(), - Entry::file(PackSource::Buffered(manifest.clone())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Buffered(manifest.clone()), + )), ); let icon = params.icon().await?; let icon_ext = icon @@ -483,22 +491,28 @@ pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { .to_string_lossy(); files.insert( InternedString::from_display(&lazy_format!("icon.{}", icon_ext)), - Entry::file(PackSource::File(icon)), + Entry::file(TmpSource::new(tmp_dir.clone(), PackSource::File(icon))), ); files.insert( "LICENSE.md".into(), - Entry::file(PackSource::File(params.license())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::File(params.license()), + )), ); files.insert( "instructions.md".into(), - Entry::file(PackSource::File(params.instructions())), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::File(params.instructions()), + )), ); files.insert( "javascript.squashfs".into(), - Entry::file(PackSource::Squashfs(Arc::new(SqfsDir::new( - js_dir, - tmpdir.clone(), - )))), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Squashfs(Arc::new(SqfsDir::new(js_dir, tmp_dir.clone()))), + )), ); let mut s9pk = S9pk::new( @@ -511,26 +525,29 @@ pub async fn pack(ctx: CliContext, params: PackParams) -> Result<(), Error> { for assets in s9pk.as_manifest().assets.clone() { s9pk.as_archive_mut().contents_mut().insert_path( Path::new("assets").join(&assets).with_extension("squashfs"), - Entry::file(PackSource::Squashfs(Arc::new(SqfsDir::new( - assets_dir.join(&assets), - tmpdir.clone(), - )))), + Entry::file(TmpSource::new( + tmp_dir.clone(), + PackSource::Squashfs(Arc::new(SqfsDir::new( + assets_dir.join(&assets), + tmp_dir.clone(), + ))), + )), )?; } - s9pk.load_images(&*tmpdir).await?; + s9pk.load_images(tmp_dir.clone()).await?; s9pk.validate_and_filter(None)?; s9pk.serialize( - &mut File::create(params.output(&s9pk.as_manifest().id)).await?, + &mut create_file(params.output(&s9pk.as_manifest().id)).await?, false, ) .await?; drop(s9pk); - tmpdir.gc().await?; + tmp_dir.gc().await?; Ok(()) } diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index 92564b8e4..b85bd8d6d 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -11,7 +11,6 @@ use persistent_container::PersistentContainer; use rpc_toolkit::{from_fn_async, CallRemoteHandler, Empty, HandlerArgs, HandlerFor}; use serde::{Deserialize, Serialize}; use start_stop::StartStop; -use tokio::fs::File; use tokio::sync::Notify; use ts_rs::TS; @@ -33,6 +32,7 @@ use crate::status::MainStatus; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::concurrent::ConcurrentActor; use crate::util::actor::Actor; +use crate::util::io::create_file; use crate::util::serde::Pem; use crate::volume::data_dir; @@ -403,7 +403,7 @@ impl Service { #[instrument(skip_all)] pub async fn backup(&self, guard: impl GenericMountGuard) -> Result<(), Error> { let id = &self.seed.id; - let mut file = File::create(guard.path().join(id).with_extension("s9pk")).await?; + let mut file = create_file(guard.path().join(id).with_extension("s9pk")).await?; self.seed .persistent_container .s9pk diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index d9d08e5d3..e0b31ea97 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -9,14 +9,12 @@ use helpers::NonDetachingJoinHandle; use models::{ImageId, ProcedureName, VolumeId}; use rpc_toolkit::{Empty, Server, ShutdownHandle}; use serde::de::DeserializeOwned; -use tokio::fs::File; use tokio::process::Command; use tokio::sync::{oneshot, watch, Mutex, OnceCell}; use tracing::instrument; use super::service_effect_handler::{service_effect_handler, EffectContext}; use super::transition::{TransitionKind, TransitionState}; -use super::ServiceActorSeed; use crate::context::RpcContext; use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::idmapped::IdMapped; @@ -32,6 +30,7 @@ use crate::s9pk::merkle_archive::source::FileSource; use crate::s9pk::S9pk; use crate::service::start_stop::StartStop; use crate::service::{rpc, RunningStatus, Service}; +use crate::util::io::create_file; use crate::util::rpc_client::UnixRpcClient; use crate::util::Invoke; use crate::volume::{asset_dir, data_dir}; @@ -237,7 +236,7 @@ impl PersistentContainer { .get_path(Path::new("images").join(arch).join(&env_filename)) .and_then(|e| e.as_file()) { - env.copy(&mut File::create(image_path.join(&env_filename)).await?) + env.copy(&mut create_file(image_path.join(&env_filename)).await?) .await?; } let json_filename = Path::new(image.as_ref()).with_extension("json"); @@ -247,7 +246,7 @@ impl PersistentContainer { .get_path(Path::new("images").join(arch).join(&json_filename)) .and_then(|e| e.as_file()) { - json.copy(&mut File::create(image_path.join(&json_filename)).await?) + json.copy(&mut create_file(image_path.join(&json_filename)).await?) .await?; } } diff --git a/core/startos/src/service/service_effect_handler.rs b/core/startos/src/service/service_effect_handler.rs index 63c313ead..7547584e0 100644 --- a/core/startos/src/service/service_effect_handler.rs +++ b/core/startos/src/service/service_effect_handler.rs @@ -7,8 +7,8 @@ use std::str::FromStr; use std::sync::{Arc, Weak}; use clap::builder::ValueParserFactory; -use clap::{CommandFactory, FromArgMatches, Parser}; -use emver::VersionRange; +use clap::Parser; +use exver::VersionRange; use imbl_value::json; use itertools::Itertools; use models::{ @@ -1383,7 +1383,7 @@ struct CheckDependenciesResult { is_running: bool, health_checks: Vec, #[ts(type = "string | null")] - version: Option, + version: Option, } async fn check_dependencies( diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index f8874d21a..02244169c 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -94,12 +94,19 @@ impl ServiceMap { } #[instrument(skip_all)] - pub async fn install( + pub async fn install( &self, ctx: RpcContext, - mut s9pk: S9pk, + s9pk: F, recovery_source: Option, - ) -> Result { + progress: Option, + ) -> Result + where + F: FnOnce() -> Fut, + Fut: Future, Error>>, + S: FileSource + Clone, + { + let mut s9pk = s9pk().await?; s9pk.validate_and_filter(ctx.s9pk_arch)?; let manifest = s9pk.as_manifest().clone(); let id = manifest.id.clone(); @@ -118,7 +125,7 @@ impl ServiceMap { }; let size = s9pk.size(); - let progress = FullProgressTracker::new(); + let progress = progress.unwrap_or_else(|| FullProgressTracker::new()); let download_progress_contribution = size.unwrap_or(60); let mut download_progress = progress.add_phase( InternedString::intern("Download"), diff --git a/core/startos/src/setup.rs b/core/startos/src/setup.rs index 2b701c01f..8fb7cf7c1 100644 --- a/core/startos/src/setup.rs +++ b/core/startos/src/setup.rs @@ -8,7 +8,6 @@ use patch_db::json_ptr::ROOT; use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use tokio::io::AsyncWriteExt; use tokio::try_join; use tracing::instrument; @@ -35,7 +34,7 @@ use crate::prelude::*; use crate::progress::{FullProgress, PhaseProgressTrackerHandle}; use crate::rpc_continuations::Guid; use crate::util::crypto::EncryptedWire; -use crate::util::io::{dir_copy, dir_size, Counter}; +use crate::util::io::{create_file, dir_copy, dir_size, Counter}; use crate::{Error, ErrorKind, ResultExt}; pub fn setup() -> ParentHandler { @@ -324,7 +323,7 @@ pub async fn execute( pub async fn complete(ctx: SetupContext) -> Result { match ctx.result.get() { Some(Ok((res, ctx))) => { - let mut guid_file = File::create("/media/startos/config/disk.guid").await?; + let mut guid_file = create_file("/media/startos/config/disk.guid").await?; guid_file.write_all(ctx.disk_guid.as_bytes()).await?; guid_file.sync_all().await?; Ok(res.clone()) diff --git a/core/startos/src/ssh.rs b/core/startos/src/ssh.rs index 82d06be4c..b97fba3e4 100644 --- a/core/startos/src/ssh.rs +++ b/core/startos/src/ssh.rs @@ -13,6 +13,7 @@ use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::prelude::*; use crate::util::clap::FromStrParser; +use crate::util::io::create_file; use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat}; pub const SSH_AUTHORIZED_KEYS_FILE: &str = "/home/start9/.ssh/authorized_keys"; @@ -229,7 +230,7 @@ pub async fn sync_keys>(keys: &SshKeys, dest: P) -> Result<(), Er if tokio::fs::metadata(ssh_dir).await.is_err() { tokio::fs::create_dir_all(ssh_dir).await?; } - let mut f = tokio::fs::File::create(dest).await?; + let mut f = create_file(dest).await?; for key in keys.0.values() { f.write_all(key.0.to_key_format().as_bytes()).await?; f.write_all(b"\n").await?; diff --git a/core/startos/src/system.rs b/core/startos/src/system.rs index 50b17b54a..7a8cb9afe 100644 --- a/core/startos/src/system.rs +++ b/core/startos/src/system.rs @@ -20,6 +20,7 @@ use crate::prelude::*; use crate::rpc_continuations::RpcContinuations; use crate::shutdown::Shutdown; use crate::util::cpupower::{get_available_governors, set_governor, Governor}; +use crate::util::io::open_file; use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat}; use crate::util::Invoke; @@ -657,7 +658,7 @@ impl ProcStat { async fn get_proc_stat() -> Result { use tokio::io::AsyncBufReadExt; let mut cpu_line = String::new(); - let _n = tokio::io::BufReader::new(tokio::fs::File::open("/proc/stat").await?) + let _n = tokio::io::BufReader::new(open_file("/proc/stat").await?) .read_line(&mut cpu_line) .await?; let stats: Vec = cpu_line diff --git a/core/startos/src/update/mod.rs b/core/startos/src/update/mod.rs index dc164d2cd..79d852754 100644 --- a/core/startos/src/update/mod.rs +++ b/core/startos/src/update/mod.rs @@ -4,8 +4,8 @@ use std::time::Duration; use clap::{ArgAction, Parser}; use color_eyre::eyre::{eyre, Result}; -use emver::{Version, VersionRange}; -use futures::{FutureExt, TryStreamExt}; +use exver::{Version, VersionRange}; +use futures::TryStreamExt; use helpers::{AtomicFile, NonDetachingJoinHandle}; use imbl_value::json; use itertools::Itertools; diff --git a/core/startos/src/upload.rs b/core/startos/src/upload.rs index b922ab9d2..4735a63fb 100644 --- a/core/startos/src/upload.rs +++ b/core/startos/src/upload.rs @@ -1,3 +1,4 @@ +use std::io::SeekFrom; use std::pin::Pin; use std::sync::Arc; use std::task::Poll; @@ -5,20 +6,20 @@ use std::time::Duration; use axum::body::Body; use axum::response::Response; -use futures::StreamExt; +use futures::{ready, FutureExt, StreamExt}; use http::header::CONTENT_LENGTH; use http::StatusCode; use imbl_value::InternedString; use tokio::fs::File; -use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt}; use tokio::sync::watch; use crate::context::RpcContext; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation}; -use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; +use crate::s9pk::merkle_archive::source::multi_cursor_file::{FileCursor, MultiCursorFile}; use crate::s9pk::merkle_archive::source::ArchiveSource; -use crate::util::io::TmpDir; +use crate::util::io::{create_file, TmpDir}; pub async fn upload( ctx: &RpcContext, @@ -215,14 +216,15 @@ impl UploadingFile { pub async fn new() -> Result<(UploadHandle, Self), Error> { let progress = watch::channel(Progress::default()); let tmp_dir = Arc::new(TmpDir::new().await?); - let file = File::create(tmp_dir.join("upload.tmp")).await?; + let file = create_file(tmp_dir.join("upload.tmp")).await?; let uploading = Self { - tmp_dir, + tmp_dir: tmp_dir.clone(), file: MultiCursorFile::open(&file).await?, progress: progress.1, }; Ok(( UploadHandle { + tmp_dir, file, progress: progress.0, }, @@ -237,22 +239,127 @@ impl UploadingFile { } } impl ArchiveSource for UploadingFile { - type Reader = ::Reader; + type FetchReader = ::FetchReader; + type FetchAllReader = UploadingFileReader; async fn size(&self) -> Option { Progress::expected_size(&mut self.progress.clone()).await } - async fn fetch_all(&self) -> Result { - Progress::ready(&mut self.progress.clone()).await?; - self.file.fetch_all().await + async fn fetch_all(&self) -> Result { + let mut file = self.file.cursor().await?; + file.seek(SeekFrom::Start(0)).await?; + Ok(UploadingFileReader { + tmp_dir: self.tmp_dir.clone(), + file, + position: 0, + to_seek: None, + progress: self.progress.clone(), + }) } - async fn fetch(&self, position: u64, size: u64) -> Result { + async fn fetch(&self, position: u64, size: u64) -> Result { Progress::ready_for(&mut self.progress.clone(), position + size).await?; self.file.fetch(position, size).await } } +#[pin_project::pin_project(project = UploadingFileReaderProjection)] +pub struct UploadingFileReader { + tmp_dir: Arc, + position: u64, + to_seek: Option, + #[pin] + file: FileCursor, + progress: watch::Receiver, +} +impl<'a> UploadingFileReaderProjection<'a> { + fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Result { + let ready = Progress::ready(&mut *self.progress); + tokio::pin!(ready); + Ok(ready + .poll_unpin(cx) + .map_err(|e| std::io::Error::other(e.source))? + .is_ready()) + } + fn poll_ready_for( + &mut self, + cx: &mut std::task::Context<'_>, + size: u64, + ) -> Result { + let ready = Progress::ready_for(&mut *self.progress, size); + tokio::pin!(ready); + Ok(ready + .poll_unpin(cx) + .map_err(|e| std::io::Error::other(e.source))? + .is_ready()) + } +} +impl AsyncRead for UploadingFileReader { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> Poll> { + let mut this = self.project(); + + let position = *this.position; + if this.poll_ready(cx)? || this.poll_ready_for(cx, position + buf.remaining() as u64)? { + let start = buf.filled().len(); + let res = this.file.poll_read(cx, buf); + *this.position += (buf.filled().len() - start) as u64; + res + } else { + Poll::Pending + } + } +} +impl AsyncSeek for UploadingFileReader { + fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> { + let this = self.project(); + *this.to_seek = Some(position); + Ok(()) + } + fn poll_complete( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + let mut this = self.project(); + if let Some(to_seek) = *this.to_seek { + let size = match to_seek { + SeekFrom::Current(n) => (*this.position as i64 + n) as u64, + SeekFrom::Start(n) => n, + SeekFrom::End(n) => { + let expected_size = this.progress.borrow().expected_size; + match expected_size { + Some(end) => (end as i64 + n) as u64, + None => { + if !this.poll_ready(cx)? { + return Poll::Pending; + } + (this.progress.borrow().expected_size.ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + eyre!("upload maked complete without expected size"), + ) + })? as i64 + + n) as u64 + } + } + } + }; + if !this.poll_ready_for(cx, size)? { + return Poll::Pending; + } + } + if let Some(seek) = this.to_seek.take() { + this.file.as_mut().start_seek(seek)?; + } + *this.position = ready!(this.file.as_mut().poll_complete(cx)?); + Poll::Ready(Ok(*this.position)) + } +} + #[pin_project::pin_project(PinnedDrop)] pub struct UploadHandle { + tmp_dir: Arc, #[pin] file: File, progress: watch::Sender, diff --git a/core/startos/src/util/io.rs b/core/startos/src/util/io.rs index b5748d1d9..1ec789f23 100644 --- a/core/startos/src/util/io.rs +++ b/core/startos/src/util/io.rs @@ -610,13 +610,13 @@ pub fn dir_copy<'a, P0: AsRef + 'a + Send + Sync, P1: AsRef + 'a + S let src_path = e.path(); let dst_path = dst_path.join(e.file_name()); if m.is_file() { - let mut dst_file = tokio::fs::File::create(&dst_path).await.with_ctx(|_| { + let mut dst_file = create_file(&dst_path).await.with_ctx(|_| { ( crate::ErrorKind::Filesystem, format!("create {}", dst_path.display()), ) })?; - let mut rdr = tokio::fs::File::open(&src_path).await.with_ctx(|_| { + let mut rdr = open_file(&src_path).await.with_ctx(|_| { ( crate::ErrorKind::Filesystem, format!("open {}", src_path.display()), @@ -829,6 +829,13 @@ impl Drop for TmpDir { } } +pub async fn open_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + File::open(path) + .await + .with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("open {path:?}"))) +} + pub async fn create_file(path: impl AsRef) -> Result { let path = path.as_ref(); if let Some(parent) = path.parent() { diff --git a/core/startos/src/util/mod.rs b/core/startos/src/util/mod.rs index f2334632e..7200eaaba 100644 --- a/core/startos/src/util/mod.rs +++ b/core/startos/src/util/mod.rs @@ -26,6 +26,7 @@ use tokio::sync::{oneshot, Mutex, OwnedMutexGuard, RwLock}; use tracing::instrument; use crate::shutdown::Shutdown; +use crate::util::io::create_file; use crate::{Error, ErrorKind, ResultExt as _}; pub mod actor; pub mod clap; @@ -385,16 +386,16 @@ impl SOption for SNone {} #[async_trait] pub trait AsyncFileExt: Sized { - async fn maybe_open + Send + Sync>(path: P) -> std::io::Result>; + async fn maybe_open + Send + Sync>(path: P) -> Result, Error>; async fn delete + Send + Sync>(path: P) -> std::io::Result<()>; } #[async_trait] impl AsyncFileExt for File { - async fn maybe_open + Send + Sync>(path: P) -> std::io::Result> { - match File::open(path).await { + async fn maybe_open + Send + Sync>(path: P) -> Result, Error> { + match File::open(path.as_ref()).await { Ok(f) => Ok(Some(f)), Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None), - Err(e) => Err(e), + Err(e) => Err(e).with_ctx(|_| (ErrorKind::Filesystem, path.as_ref().display())), } } async fn delete + Send + Sync>(path: P) -> std::io::Result<()> { @@ -590,9 +591,7 @@ impl FileLock { .await .with_ctx(|_| (crate::ErrorKind::Filesystem, parent.display().to_string()))?; } - let f = File::create(&path) - .await - .with_ctx(|_| (crate::ErrorKind::Filesystem, path.display().to_string()))?; + let f = create_file(&path).await?; let file_guard = tokio::task::spawn_blocking(move || { fd_lock_rs::FdLock::lock(f, fd_lock_rs::LockType::Exclusive, blocking) }) diff --git a/core/startos/src/util/rpc.rs b/core/startos/src/util/rpc.rs index e80aac6cb..54664833b 100644 --- a/core/startos/src/util/rpc.rs +++ b/core/startos/src/util/rpc.rs @@ -3,7 +3,6 @@ use std::path::Path; use clap::Parser; use rpc_toolkit::{from_fn_async, Context, ParentHandler}; use serde::{Deserialize, Serialize}; -use tokio::fs::File; use url::Url; use crate::context::CliContext; @@ -11,7 +10,7 @@ use crate::prelude::*; use crate::s9pk::merkle_archive::source::http::HttpSource; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; use crate::s9pk::merkle_archive::source::ArchiveSource; -use crate::util::io::ParallelBlake3Writer; +use crate::util::io::{open_file, ParallelBlake3Writer}; use crate::util::serde::Base16; use crate::util::Apply; use crate::CAP_10_MiB; @@ -40,7 +39,7 @@ pub async fn b3sum( path: impl AsRef, allow_mmap: bool, ) -> Result, Error> { - let file = MultiCursorFile::from(File::open(path).await?); + let file = MultiCursorFile::from(open_file(path).await?); if allow_mmap { return file.blake3_mmap().await.map(|h| *h.as_bytes()).map(Base16); } diff --git a/core/startos/src/util/serde.rs b/core/startos/src/util/serde.rs index 5d1289e7e..b3b2dea6a 100644 --- a/core/startos/src/util/serde.rs +++ b/core/startos/src/util/serde.rs @@ -1,4 +1,3 @@ -use std::any::TypeId; use std::collections::VecDeque; use std::marker::PhantomData; use std::ops::Deref; @@ -9,10 +8,9 @@ use clap::{ArgMatches, CommandFactory, FromArgMatches}; use color_eyre::eyre::eyre; use imbl::OrdMap; use openssl::pkey::{PKey, Private}; -use openssl::x509::{X509Ref, X509}; +use openssl::x509::X509; use rpc_toolkit::{ - CliBindings, Context, Handler, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, - PrintCliResult, + CliBindings, Context, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, PrintCliResult, }; use serde::de::DeserializeOwned; use serde::ser::{SerializeMap, SerializeSeq}; @@ -1188,7 +1186,7 @@ pub trait PemEncoding: Sized { impl PemEncoding for X509 { fn from_pem(pem: &str) -> Result { - X509::from_pem(pem.as_bytes()).map_err(E::custom) + Self::from_pem(pem.as_bytes()).map_err(E::custom) } fn to_pem(&self) -> Result { String::from_utf8((&**self).to_pem().map_err(E::custom)?).map_err(E::custom) @@ -1197,7 +1195,7 @@ impl PemEncoding for X509 { impl PemEncoding for PKey { fn from_pem(pem: &str) -> Result { - PKey::::private_key_from_pem(pem.as_bytes()).map_err(E::custom) + Self::private_key_from_pem(pem.as_bytes()).map_err(E::custom) } fn to_pem(&self) -> Result { String::from_utf8((&**self).private_key_to_pem_pkcs8().map_err(E::custom)?) @@ -1207,7 +1205,7 @@ impl PemEncoding for PKey { impl PemEncoding for ssh_key::PrivateKey { fn from_pem(pem: &str) -> Result { - ssh_key::PrivateKey::from_openssh(pem.as_bytes()).map_err(E::custom) + Self::from_openssh(pem.as_bytes()).map_err(E::custom) } fn to_pem(&self) -> Result { self.to_openssh(ssh_key::LineEnding::LF) @@ -1219,7 +1217,7 @@ impl PemEncoding for ssh_key::PrivateKey { impl PemEncoding for ed25519_dalek::VerifyingKey { fn from_pem(pem: &str) -> Result { use ed25519_dalek::pkcs8::DecodePublicKey; - ed25519_dalek::VerifyingKey::from_public_key_pem(pem).map_err(E::custom) + Self::from_public_key_pem(pem).map_err(E::custom) } fn to_pem(&self) -> Result { use ed25519_dalek::pkcs8::EncodePublicKey; @@ -1228,6 +1226,19 @@ impl PemEncoding for ed25519_dalek::VerifyingKey { } } +impl PemEncoding for ed25519_dalek::SigningKey { + fn from_pem(pem: &str) -> Result { + use ed25519_dalek::pkcs8::DecodePrivateKey; + Self::from_pkcs8_pem(pem).map_err(E::custom) + } + fn to_pem(&self) -> Result { + use ed25519_dalek::pkcs8::EncodePrivateKey; + self.to_pkcs8_pem(pkcs8::LineEnding::LF) + .map_err(E::custom) + .map(|s| s.as_str().to_owned()) + } +} + pub mod pem { use serde::{Deserialize, Deserializer, Serializer}; diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index d063558e2..003a44326 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -25,20 +25,20 @@ enum Version { V0_3_5_1(Wrapper), V0_3_5_2(Wrapper), V0_3_6(Wrapper), - Other(emver::Version), + Other(exver::Version), } impl Version { - fn from_util_version(version: crate::util::VersionString) -> Self { + fn from_exver_version(version: exver::Version) -> Self { serde_json::to_value(version.clone()) .and_then(serde_json::from_value) .unwrap_or_else(|_e| { tracing::warn!("Can't deserialize: {:?} and falling back to other", version); - Version::Other(version.into_version()) + Version::Other(version) }) } #[cfg(test)] - fn as_sem_ver(&self) -> emver::Version { + fn as_exver(&self) -> exver::Version { match self { Version::LT0_3_5(LTWrapper(_, x)) => x.clone(), Version::V0_3_5(Wrapper(x)) => x.semver(), @@ -56,8 +56,8 @@ where { type Previous: VersionT; fn new() -> Self; - fn semver(&self) -> emver::Version; - fn compat(&self) -> &'static emver::VersionRange; + fn semver(&self) -> exver::Version; + fn compat(&self) -> &'static exver::VersionRange; fn up(&self, db: &TypedPatchDb) -> impl Future> + Send; fn down(&self, db: &TypedPatchDb) -> impl Future> + Send; fn commit( @@ -158,7 +158,7 @@ where } #[derive(Debug, Clone)] -struct LTWrapper(T, emver::Version); +struct LTWrapper(T, exver::Version); impl serde::Serialize for LTWrapper where T: VersionT, @@ -172,10 +172,10 @@ where T: VersionT, { fn deserialize>(deserializer: D) -> Result { - let v = crate::util::VersionString::deserialize(deserializer)?; + let v = exver::Version::deserialize(deserializer)?; let version = T::new(); - if *v < version.semver() { - Ok(Self(version, v.into_version())) + if v < version.semver() { + Ok(Self(version, v)) } else { Err(serde::de::Error::custom("Mismatched Version")) } @@ -197,9 +197,9 @@ where T: VersionT, { fn deserialize>(deserializer: D) -> Result { - let v = crate::util::VersionString::deserialize(deserializer)?; + let v = exver::Version::deserialize(deserializer)?; let version = T::new(); - if *v == version.semver() { + if v == version.semver() { Ok(Wrapper(version)) } else { Err(serde::de::Error::custom("Mismatched Version")) @@ -212,7 +212,7 @@ pub async fn init( mut progress: PhaseProgressTrackerHandle, ) -> Result<(), Error> { progress.start(); - let version = Version::from_util_version( + let version = Version::from_exver_version( db.peek() .await .as_public() @@ -256,9 +256,18 @@ mod tests { use super::*; - fn em_version() -> impl Strategy { - any::<(usize, usize, usize, usize)>().prop_map(|(major, minor, patch, super_minor)| { - emver::Version::new(major, minor, patch, super_minor) + fn em_version() -> impl Strategy { + any::<(usize, usize, usize, bool)>().prop_map(|(major, minor, patch, alpha)| { + if alpha { + exver::Version::new( + [0, major, minor] + .into_iter() + .chain(Some(patch).filter(|n| *n != 0)), + [], + ) + } else { + exver::Version::new([major, minor, patch], []) + } }) } @@ -273,15 +282,15 @@ mod tests { proptest! { #[test] - fn emversion_isomorphic_version(original in em_version()) { - let version = Version::from_util_version(original.clone().into()); - let back = version.as_sem_ver(); + fn exversion_isomorphic_version(original in em_version()) { + let version = Version::from_exver_version(original.clone().into()); + let back = version.as_exver(); prop_assert_eq!(original, back, "All versions should round trip"); } #[test] fn version_isomorphic_em_version(version in versions()) { - let sem_ver = version.as_sem_ver(); - let back = Version::from_util_version(sem_ver.into()); + let sem_ver = version.as_exver(); + let back = Version::from_exver_version(sem_ver.into()); prop_assert_eq!(format!("{:?}",version), format!("{:?}", back), "All versions should round trip"); } } diff --git a/core/startos/src/version/v0_3_5.rs b/core/startos/src/version/v0_3_5.rs index 21be1ca49..167132784 100644 --- a/core/startos/src/version/v0_3_5.rs +++ b/core/startos/src/version/v0_3_5.rs @@ -1,4 +1,4 @@ -use emver::VersionRange; +use exver::{ExtendedVersion, VersionRange}; use super::VersionT; use crate::db::model::Database; @@ -6,17 +6,25 @@ use crate::prelude::*; use crate::version::Current; lazy_static::lazy_static! { - pub static ref V0_3_0_COMPAT: VersionRange = VersionRange::Conj( - Box::new(VersionRange::Anchor( - emver::GTE, - emver::Version::new(0, 3, 0, 0), - )), - Box::new(VersionRange::Anchor(emver::LTE, Current::new().semver())), + pub static ref V0_3_0_COMPAT: VersionRange = VersionRange::and( + VersionRange::anchor( + exver::GTE, + ExtendedVersion::new( + exver::Version::new([0, 3, 0], []), + exver::Version::default(), + ), + ), + VersionRange::anchor( + exver::LTE, + ExtendedVersion::new( + Current::new().semver(), + exver::Version::default(), + ) + ), ); + static ref V0_3_5: exver::Version = exver::Version::new([0, 3, 5], []); } -const V0_3_5: emver::Version = emver::Version::new(0, 3, 5, 0); - #[derive(Clone, Debug)] pub struct Version; @@ -25,8 +33,8 @@ impl VersionT for Version { fn new() -> Self { Version } - fn semver(&self) -> emver::Version { - V0_3_5 + fn semver(&self) -> exver::Version { + V0_3_5.clone() } fn compat(&self) -> &'static VersionRange { &V0_3_0_COMPAT diff --git a/core/startos/src/version/v0_3_5_1.rs b/core/startos/src/version/v0_3_5_1.rs index d0f99c153..c6c328f6d 100644 --- a/core/startos/src/version/v0_3_5_1.rs +++ b/core/startos/src/version/v0_3_5_1.rs @@ -1,11 +1,13 @@ -use emver::VersionRange; +use exver::VersionRange; use super::v0_3_5::V0_3_0_COMPAT; use super::{v0_3_5, VersionT}; use crate::db::model::Database; use crate::prelude::*; -const V0_3_5_1: emver::Version = emver::Version::new(0, 3, 5, 1); +lazy_static::lazy_static! { + static ref V0_3_5_1: exver::Version = exver::Version::new([0, 3, 5, 1], []); +} #[derive(Clone, Debug)] pub struct Version; @@ -15,8 +17,8 @@ impl VersionT for Version { fn new() -> Self { Version } - fn semver(&self) -> emver::Version { - V0_3_5_1 + fn semver(&self) -> exver::Version { + V0_3_5_1.clone() } fn compat(&self) -> &'static VersionRange { &V0_3_0_COMPAT diff --git a/core/startos/src/version/v0_3_5_2.rs b/core/startos/src/version/v0_3_5_2.rs index fae689997..00143f8cd 100644 --- a/core/startos/src/version/v0_3_5_2.rs +++ b/core/startos/src/version/v0_3_5_2.rs @@ -1,11 +1,13 @@ -use emver::VersionRange; +use exver::VersionRange; use super::v0_3_5::V0_3_0_COMPAT; use super::{v0_3_5_1, VersionT}; use crate::db::model::Database; use crate::prelude::*; -const V0_3_5_2: emver::Version = emver::Version::new(0, 3, 5, 2); +lazy_static::lazy_static! { + static ref V0_3_5_2: exver::Version = exver::Version::new([0, 3, 5, 2], []); +} #[derive(Clone, Debug)] pub struct Version; @@ -15,8 +17,8 @@ impl VersionT for Version { fn new() -> Self { Version } - fn semver(&self) -> emver::Version { - V0_3_5_2 + fn semver(&self) -> exver::Version { + V0_3_5_2.clone() } fn compat(&self) -> &'static VersionRange { &V0_3_0_COMPAT diff --git a/core/startos/src/version/v0_3_6.rs b/core/startos/src/version/v0_3_6.rs index 0ff6d662a..9564a7376 100644 --- a/core/startos/src/version/v0_3_6.rs +++ b/core/startos/src/version/v0_3_6.rs @@ -1,11 +1,13 @@ -use emver::VersionRange; +use exver::VersionRange; use super::v0_3_5::V0_3_0_COMPAT; use super::{v0_3_5_1, VersionT}; use crate::db::model::Database; use crate::prelude::*; -const V0_3_6: emver::Version = emver::Version::new(0, 3, 6, 0); +lazy_static::lazy_static! { + static ref V0_3_6: exver::Version = exver::Version::new([0, 3, 6], []); +} #[derive(Clone, Debug)] pub struct Version; @@ -15,8 +17,8 @@ impl VersionT for Version { fn new() -> Self { Version } - fn semver(&self) -> emver::Version { - V0_3_6 + fn semver(&self) -> exver::Version { + V0_3_6.clone() } fn compat(&self) -> &'static VersionRange { &V0_3_0_COMPAT diff --git a/debian/postinst b/debian/postinst index 238bd9457..96e392fc8 100755 --- a/debian/postinst +++ b/debian/postinst @@ -78,6 +78,7 @@ sed -i '/\(^\|#\)Compress=/c\Compress=yes' /etc/systemd/journald.conf sed -i '/\(^\|#\)SystemMaxUse=/c\SystemMaxUse=1G' /etc/systemd/journald.conf sed -i '/\(^\|#\)ForwardToSyslog=/c\ForwardToSyslog=no' /etc/systemd/journald.conf sed -i '/^\s*#\?\s*issue_discards\s*=\s*/c\issue_discards = 1' /etc/lvm/lvm.conf +sed -i '/\(^\|#\)\s*unqualified-search-registries\s*=\s*/c\unqualified-search-registries = ["docker.io"]' /etc/containers/registries.conf mkdir -p /etc/nginx/ssl diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..497f28c49 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "embassy-os", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} diff --git a/sdk/lib/emverLite/mod.ts b/sdk/lib/emverLite/mod.ts index f69d4f35d..52fb4e347 100644 --- a/sdk/lib/emverLite/mod.ts +++ b/sdk/lib/emverLite/mod.ts @@ -2,9 +2,9 @@ import * as matches from "ts-matches" const starSub = /((\d+\.)*\d+)\.\*/ // prettier-ignore -export type ValidEmVer = `${number}${`.${number}` | ""}${`.${number}` | ""}${`-${string}` | ""}`; +export type ValidEmVer = string; // prettier-ignore -export type ValidEmVerRange = `${'>=' | '<='| '<' | '>' | ''}${'^' | '~' | ''}${number | '*'}${`.${number | '*'}` | ""}${`.${number | '*'}` | ""}${`-${string}` | ""}`; +export type ValidEmVerRange = string; function incrementLastNumber(list: number[]) { const newList = [...list] diff --git a/sdk/lib/index.browser.ts b/sdk/lib/index.browser.ts index d7c10093f..075bfcf50 100644 --- a/sdk/lib/index.browser.ts +++ b/sdk/lib/index.browser.ts @@ -1,6 +1,7 @@ export { EmVer } from "./emverLite/mod" export { setupManifest } from "./manifest/setupManifest" export { setupExposeStore } from "./store/setupExposeStore" +export { S9pk } from "./s9pk" export * as config from "./config" export * as CB from "./config/builder" export * as CT from "./config/configTypes" diff --git a/sdk/lib/index.ts b/sdk/lib/index.ts index 89d147ed1..c99798d72 100644 --- a/sdk/lib/index.ts +++ b/sdk/lib/index.ts @@ -6,6 +6,7 @@ export { setupManifest } from "./manifest/setupManifest" export { FileHelper } from "./util/fileHelper" export { setupExposeStore } from "./store/setupExposeStore" export { pathBuilder } from "./store/PathBuilder" +export { S9pk } from "./s9pk" export * as actions from "./actions" export * as backup from "./backup" diff --git a/sdk/lib/osBindings/GetPackageParams.ts b/sdk/lib/osBindings/GetPackageParams.ts index 6dfecc9b2..8b852c35c 100644 --- a/sdk/lib/osBindings/GetPackageParams.ts +++ b/sdk/lib/osBindings/GetPackageParams.ts @@ -1,10 +1,11 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { PackageDetailLevel } from "./PackageDetailLevel" import type { PackageId } from "./PackageId" +import type { Version } from "./Version" export type GetPackageParams = { id: PackageId | null version: string | null - sourceVersion: string | null + sourceVersion: Version | null otherVersions: PackageDetailLevel | null } diff --git a/sdk/lib/osBindings/Manifest.ts b/sdk/lib/osBindings/Manifest.ts index 15b96bd13..d808f47a2 100644 --- a/sdk/lib/osBindings/Manifest.ts +++ b/sdk/lib/osBindings/Manifest.ts @@ -28,6 +28,6 @@ export type Manifest = { dependencies: Dependencies hardwareRequirements: HardwareRequirements gitHash: string | null - osVersion: Version + osVersion: string hasConfig: boolean } diff --git a/sdk/lib/osBindings/PackageVersionInfo.ts b/sdk/lib/osBindings/PackageVersionInfo.ts index bdded46bd..364c530f2 100644 --- a/sdk/lib/osBindings/PackageVersionInfo.ts +++ b/sdk/lib/osBindings/PackageVersionInfo.ts @@ -4,7 +4,6 @@ import type { Description } from "./Description" import type { HardwareRequirements } from "./HardwareRequirements" import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment" import type { RegistryAsset } from "./RegistryAsset" -import type { Version } from "./Version" export type PackageVersionInfo = { title: string @@ -17,7 +16,7 @@ export type PackageVersionInfo = { upstreamRepo: string supportSite: string marketingSite: string - osVersion: Version + osVersion: string hardwareRequirements: HardwareRequirements sourceVersion: string | null s9pk: RegistryAsset diff --git a/sdk/lib/osBindings/ServerInfo.ts b/sdk/lib/osBindings/ServerInfo.ts index 92b8b9b8f..935e3a99f 100644 --- a/sdk/lib/osBindings/ServerInfo.ts +++ b/sdk/lib/osBindings/ServerInfo.ts @@ -2,7 +2,6 @@ import type { Governor } from "./Governor" import type { IpInfo } from "./IpInfo" import type { ServerStatus } from "./ServerStatus" -import type { Version } from "./Version" import type { WifiInfo } from "./WifiInfo" export type ServerInfo = { @@ -10,7 +9,7 @@ export type ServerInfo = { platform: string id: string hostname: string - version: Version + version: string lastBackup: string | null eosVersionCompat: string lanAddress: string diff --git a/sdk/lib/s9pk/index.ts b/sdk/lib/s9pk/index.ts new file mode 100644 index 000000000..eba8ed3f8 --- /dev/null +++ b/sdk/lib/s9pk/index.ts @@ -0,0 +1,67 @@ +import { DataUrl, Manifest, MerkleArchiveCommitment } from "../osBindings" +import { ArrayBufferReader, MerkleArchive } from "./merkleArchive" +import mime from "mime" + +const magicAndVersion = new Uint8Array([59, 59, 2]) + +export function compare(a: Uint8Array, b: Uint8Array) { + if (a.length !== b.length) return false + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false + } + return true +} + +export class S9pk { + private constructor( + readonly manifest: Manifest, + readonly archive: MerkleArchive, + readonly size: number, + ) {} + static async deserialize( + source: Blob, + commitment: MerkleArchiveCommitment | null, + ): Promise { + const header = new ArrayBufferReader( + await source + .slice(0, magicAndVersion.length + MerkleArchive.headerSize) + .arrayBuffer(), + ) + const magicVersion = new Uint8Array(header.next(magicAndVersion.length)) + if (!compare(magicVersion, magicAndVersion)) { + throw new Error("Invalid Magic or Unexpected Version") + } + + const archive = await MerkleArchive.deserialize( + source, + "s9pk", + header, + commitment, + ) + + const manifest = JSON.parse( + new TextDecoder().decode( + await archive.contents + .getPath(["manifest.json"]) + ?.verifiedFileContents(), + ), + ) + + return new S9pk(manifest, archive, source.length) + } + async icon(): Promise { + const iconName = Object.keys(this.archive.contents.contents).find( + (name) => + name.startsWith("icon.") && mime.getType(name)?.startsWith("image/"), + ) + if (!iconName) { + throw new Error("no icon found in archive") + } + return ( + `data:${mime.getType(iconName)};base64,` + + Buffer.from( + await this.archive.contents.getPath([iconName])!.verifiedFileContents(), + ).toString("base64") + ) + } +} diff --git a/sdk/lib/s9pk/merkleArchive/directoryContents.ts b/sdk/lib/s9pk/merkleArchive/directoryContents.ts new file mode 100644 index 000000000..dab7ef53a --- /dev/null +++ b/sdk/lib/s9pk/merkleArchive/directoryContents.ts @@ -0,0 +1,80 @@ +import { ArrayBufferReader, Entry } from "." +import { blake3 } from "@noble/hashes/blake3" +import { serializeVarint } from "./varint" +import { FileContents } from "./fileContents" +import { compare } from ".." + +export class DirectoryContents { + static readonly headerSize = + 8 + // position: u64 BE + 8 // size: u64 BE + private constructor(readonly contents: { [name: string]: Entry }) {} + static async deserialize( + source: Blob, + header: ArrayBufferReader, + sighash: Uint8Array, + maxSize: bigint, + ): Promise { + const position = header.nextU64() + const size = header.nextU64() + if (size > maxSize) { + throw new Error("size is greater than signed") + } + + const tocReader = new ArrayBufferReader( + await source + .slice(Number(position), Number(position + size)) + .arrayBuffer(), + ) + const len = tocReader.nextVarint() + const entries: { [name: string]: Entry } = {} + for (let i = 0; i < len; i++) { + const name = tocReader.nextVarstring() + const entry = await Entry.deserialize(source, tocReader) + entries[name] = entry + } + + const res = new DirectoryContents(entries) + + if (!compare(res.sighash(), sighash)) { + throw new Error("hash sum does not match") + } + + return res + } + sighash(): Uint8Array { + const hasher = blake3.create({}) + const names = Object.keys(this.contents).sort() + hasher.update(new Uint8Array(serializeVarint(names.length))) + for (const name of names) { + const entry = this.contents[name] + const nameBuf = new TextEncoder().encode(name) + hasher.update(new Uint8Array(serializeVarint(nameBuf.length))) + hasher.update(nameBuf) + hasher.update(new Uint8Array(entry.hash)) + const sizeBuf = new Uint8Array(8) + new DataView(sizeBuf.buffer).setBigUint64(0, entry.size) + hasher.update(sizeBuf) + hasher.update(new Uint8Array([0])) + } + + return hasher.digest() + } + getPath(path: string[]): Entry | null { + if (path.length === 0) { + return null + } + const next = this.contents[path[0]] + const rest = path.slice(1) + if (next === undefined) { + return null + } + if (rest.length === 0) { + return next + } + if (next.contents instanceof DirectoryContents) { + return next.contents.getPath(rest) + } + return null + } +} diff --git a/sdk/lib/s9pk/merkleArchive/fileContents.ts b/sdk/lib/s9pk/merkleArchive/fileContents.ts new file mode 100644 index 000000000..7a936f1e8 --- /dev/null +++ b/sdk/lib/s9pk/merkleArchive/fileContents.ts @@ -0,0 +1,24 @@ +import { blake3 } from "@noble/hashes/blake3" +import { ArrayBufferReader } from "." +import { compare } from ".." + +export class FileContents { + private constructor(readonly contents: Blob) {} + static deserialize( + source: Blob, + header: ArrayBufferReader, + size: bigint, + ): FileContents { + const position = header.nextU64() + return new FileContents( + source.slice(Number(position), Number(position + size)), + ) + } + async verified(hash: Uint8Array): Promise { + const res = await this.contents.arrayBuffer() + if (!compare(hash, blake3(new Uint8Array(res)))) { + throw new Error("hash sum mismatch") + } + return res + } +} diff --git a/sdk/lib/s9pk/merkleArchive/index.ts b/sdk/lib/s9pk/merkleArchive/index.ts new file mode 100644 index 000000000..068363599 --- /dev/null +++ b/sdk/lib/s9pk/merkleArchive/index.ts @@ -0,0 +1,167 @@ +import { MerkleArchiveCommitment } from "../../osBindings" +import { DirectoryContents } from "./directoryContents" +import { FileContents } from "./fileContents" +import { ed25519ph } from "@noble/curves/ed25519" +import { sha512 } from "@noble/hashes/sha2" +import { VarIntProcessor } from "./varint" +import { compare } from ".." + +const maxVarstringLen = 1024 * 1024 + +export type Signer = { + pubkey: Uint8Array + signature: Uint8Array + maxSize: bigint + context: string +} + +export class ArrayBufferReader { + constructor(private buffer: ArrayBuffer) {} + next(length: number): ArrayBuffer { + const res = this.buffer.slice(0, length) + this.buffer = this.buffer.slice(length) + return res + } + nextU64(): bigint { + return new DataView(this.next(8)).getBigUint64(0) + } + nextVarint(): number { + const p = new VarIntProcessor() + while (!p.finished()) { + p.push(new Uint8Array(this.buffer.slice(0, 1))[0]) + this.buffer = this.buffer.slice(1) + } + const res = p.decode() + if (res === null) { + throw new Error("Reached EOF") + } + return res + } + nextVarstring(): string { + const len = Math.min(this.nextVarint(), maxVarstringLen) + return new TextDecoder().decode(this.next(len)) + } +} + +export class MerkleArchive { + static readonly headerSize = + 32 + // pubkey + 64 + // signature + 32 + // sighash + 8 + // size + DirectoryContents.headerSize + private constructor( + readonly signer: Signer, + readonly contents: DirectoryContents, + ) {} + static async deserialize( + source: Blob, + context: string, + header: ArrayBufferReader, + commitment: MerkleArchiveCommitment | null, + ): Promise { + const pubkey = new Uint8Array(header.next(32)) + const signature = new Uint8Array(header.next(64)) + const sighash = new Uint8Array(header.next(32)) + const rootMaxSizeBytes = header.next(8) + const maxSize = new DataView(rootMaxSizeBytes).getBigUint64(0) + + if ( + !ed25519ph.verify( + signature, + new Uint8Array( + await new Blob([sighash, rootMaxSizeBytes]).arrayBuffer(), + ), + pubkey, + { + context: new TextEncoder().encode(context), + zip215: true, + }, + ) + ) { + throw new Error("signature verification failed") + } + + if (commitment) { + if ( + !compare( + sighash, + new Uint8Array(Buffer.from(commitment.rootSighash, "base64").buffer), + ) + ) { + throw new Error("merkle root mismatch") + } + if (maxSize > commitment.rootMaxsize) { + throw new Error("root directory max size too large") + } + } else if (maxSize > 1024 * 1024) { + throw new Error( + "root directory max size over 1MiB, cancelling download in case of DOS attack", + ) + } + + const contents = await DirectoryContents.deserialize( + source, + header, + sighash, + maxSize, + ) + + return new MerkleArchive( + { + pubkey, + signature, + maxSize, + context, + }, + contents, + ) + } +} + +export class Entry { + private constructor( + readonly hash: Uint8Array, + readonly size: bigint, + readonly contents: EntryContents, + ) {} + static async deserialize( + source: Blob, + header: ArrayBufferReader, + ): Promise { + const hash = new Uint8Array(header.next(32)) + const size = header.nextU64() + const contents = await deserializeEntryContents(source, header, hash, size) + + return new Entry(new Uint8Array(hash), size, contents) + } + async verifiedFileContents(): Promise { + if (!this.contents) { + throw new Error("file is missing from archive") + } + if (!(this.contents instanceof FileContents)) { + throw new Error("is not a regular file") + } + return this.contents.verified(this.hash) + } +} + +export type EntryContents = null | FileContents | DirectoryContents +async function deserializeEntryContents( + source: Blob, + header: ArrayBufferReader, + hash: Uint8Array, + size: bigint, +): Promise { + const typeId = new Uint8Array(header.next(1))[0] + switch (typeId) { + case 0: + return null + case 1: + return FileContents.deserialize(source, header, size) + case 2: + return DirectoryContents.deserialize(source, header, hash, size) + default: + throw new Error(`Unknown type id ${typeId} found in MerkleArchive`) + } +} diff --git a/sdk/lib/s9pk/merkleArchive/varint.ts b/sdk/lib/s9pk/merkleArchive/varint.ts new file mode 100644 index 000000000..2bf4793b1 --- /dev/null +++ b/sdk/lib/s9pk/merkleArchive/varint.ts @@ -0,0 +1,62 @@ +const msb = 0x80 +const dropMsb = 0x7f +const maxSize = Math.floor((8 * 8 + 7) / 7) + +export class VarIntProcessor { + private buf: Uint8Array + private i: number + constructor() { + this.buf = new Uint8Array(maxSize) + this.i = 0 + } + push(b: number) { + if (this.i >= maxSize) { + throw new Error("Unterminated varint") + } + this.buf[this.i] = b + this.i += 1 + } + finished(): boolean { + return this.i > 0 && (this.buf[this.i - 1] & msb) === 0 + } + decode(): number | null { + let result = 0 + let shift = 0 + let success = false + for (let i = 0; i < this.i; i++) { + const b = this.buf[i] + const msbDropped = b & dropMsb + result |= msbDropped << shift + shift += 7 + + if ((b & msb) == 0 || shift > 9 * 7) { + success = (b & msb) === 0 + break + } + } + + if (success) { + return result + } else { + console.error(this.buf) + return null + } + } +} + +export function serializeVarint(int: number): ArrayBuffer { + const buf = new Uint8Array(maxSize) + let n = int + let i = 0 + + while (n >= msb) { + buf[i] = msb | n + i += 1 + n >>= 7 + } + + buf[i] = n + i += 1 + + return buf.slice(0, i).buffer +} diff --git a/sdk/lib/test/emverList.test.ts b/sdk/lib/test/emverList.test.ts index 43919aa83..07dbb5aaf 100644 --- a/sdk/lib/test/emverList.test.ts +++ b/sdk/lib/test/emverList.test.ts @@ -8,18 +8,14 @@ describe("EmVer", () => { checker.check("1.2") checker.check("1.2.3") checker.check("1.2.3.4") - // @ts-expect-error checker.check("1.2.3.4.5") - // @ts-expect-error checker.check("1.2.3.4.5.6") expect(checker.check("1")).toEqual(true) expect(checker.check("1.2")).toEqual(true) expect(checker.check("1.2.3.4")).toEqual(true) }) test("rangeOf('*') invalid", () => { - // @ts-expect-error expect(() => checker.check("a")).toThrow() - // @ts-expect-error expect(() => checker.check("")).toThrow() expect(() => checker.check("1..3")).toThrow() }) @@ -31,7 +27,6 @@ describe("EmVer", () => { expect(checker.check("2-beta123")).toEqual(true) expect(checker.check("2")).toEqual(true) expect(checker.check("1.2.3.5")).toEqual(true) - // @ts-expect-error expect(checker.check("1.2.3.4.1")).toEqual(true) }) @@ -58,7 +53,6 @@ describe("EmVer", () => { test(`rangeOf(">=1.2.3.4") valid`, () => { expect(checker.check("2")).toEqual(true) expect(checker.check("1.2.3.5")).toEqual(true) - // @ts-expect-error expect(checker.check("1.2.3.4.1")).toEqual(true) expect(checker.check("1.2.3.4")).toEqual(true) }) @@ -73,7 +67,6 @@ describe("EmVer", () => { test(`rangeOf("<1.2.3.4") invalid`, () => { expect(checker.check("2")).toEqual(false) expect(checker.check("1.2.3.5")).toEqual(false) - // @ts-expect-error expect(checker.check("1.2.3.4.1")).toEqual(false) expect(checker.check("1.2.3.4")).toEqual(false) }) @@ -88,7 +81,6 @@ describe("EmVer", () => { test(`rangeOf("<=1.2.3.4") invalid`, () => { expect(checker.check("2")).toEqual(false) expect(checker.check("1.2.3.5")).toEqual(false) - // @ts-expect-error expect(checker.check("1.2.3.4.1")).toEqual(false) }) @@ -196,7 +188,6 @@ describe("EmVer", () => { test(`rangeOf("!>1.2.3.4") invalid`, () => { expect(checker.check("2")).toEqual(false) expect(checker.check("1.2.3.5")).toEqual(false) - // @ts-expect-error expect(checker.check("1.2.3.4.1")).toEqual(false) }) diff --git a/sdk/lib/util/fileHelper.ts b/sdk/lib/util/fileHelper.ts index 54f1eca06..383b4fd31 100644 --- a/sdk/lib/util/fileHelper.ts +++ b/sdk/lib/util/fileHelper.ts @@ -1,7 +1,7 @@ import * as matches from "ts-matches" import * as YAML from "yaml" import * as TOML from "@iarna/toml" -import _ from "lodash" +import merge from "lodash.merge" import * as T from "../types" import * as fs from "node:fs/promises" @@ -82,7 +82,7 @@ export class FileHelper
{ async merge(data: A, effects: T.Effects) { const fileData = (await this.read(effects).catch(() => ({}))) || {} - const mergeData = _.merge({}, fileData, data) + const mergeData = merge({}, fileData, data) return await this.write(mergeData, effects) } /** diff --git a/sdk/package-lock.json b/sdk/package-lock.json index a50066b34..0a3655a7d 100644 --- a/sdk/package-lock.json +++ b/sdk/package-lock.json @@ -1,29 +1,32 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha1", + "version": "0.3.6-alpha5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha1", + "version": "0.3.6-alpha5", "license": "MIT", "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", - "ts-matches": "^5.4.1" + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", + "yaml": "^2.2.2" }, "devDependencies": { - "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", - "@types/lodash": "^4.17.5", + "@types/lodash.merge": "^4.6.2", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "tsx": "^4.7.1", - "typescript": "^5.0.4", - "yaml": "^2.2.2" + "typescript": "^5.0.4" } }, "node_modules/@ampproject/remapping": { @@ -653,8 +656,7 @@ "node_modules/@iarna/toml": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", - "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", - "dev": true + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -1006,6 +1008,28 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -1144,6 +1168,15 @@ "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", "dev": true }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "18.15.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.10.tgz", @@ -2849,17 +2882,17 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, + "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==" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -2918,6 +2951,20 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3580,9 +3627,9 @@ "dev": true }, "node_modules/ts-matches": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.4.1.tgz", - "integrity": "sha512-kXrY75F0s0WD15N2bWKDScKlKgwnusN6dTRzGs1N7LlxQRnazrsBISC1HL4sy2adsyk65Zbx3Ui3IGN8leAFOQ==" + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" }, "node_modules/ts-node": { "version": "10.9.1", @@ -4249,7 +4296,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", - "dev": true, "engines": { "node": ">= 14" } diff --git a/sdk/package.json b/sdk/package.json index d6c3a1ca5..8ed984a82 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -31,10 +31,13 @@ "homepage": "https://github.com/Start9Labs/start-sdk#readme", "dependencies": { "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", - "ts-matches": "^5.4.1", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", "yaml": "^2.2.2", - "@iarna/toml": "^2.2.5" + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0" }, "prettier": { "trailingComma": "all", @@ -44,7 +47,7 @@ }, "devDependencies": { "@types/jest": "^29.4.0", - "@types/lodash": "^4.17.5", + "@types/lodash.merge": "^4.6.2", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", diff --git a/system-images/compat/src/config/mod.rs b/system-images/compat/src/config/mod.rs index ce591b06a..c677dd3d5 100644 --- a/system-images/compat/src/config/mod.rs +++ b/system-images/compat/src/config/mod.rs @@ -55,7 +55,7 @@ pub fn validate_configuration( Ok(_) => { // create temp config file serde_yaml::to_writer( - std::fs::File::create(config_path.with_extension("tmp"))?, + std::fs::create_file(config_path.with_extension("tmp"))?, &config, )?; std::fs::rename(config_path.with_extension("tmp"), config_path)?; diff --git a/web/package-lock.json b/web/package-lock.json index ca5c8efe8..8a48b18fa 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -23,6 +23,8 @@ "@ng-web-apis/common": "^2.0.0", "@ng-web-apis/mutation-observer": "^2.0.0", "@ng-web-apis/resize-observer": "^2.0.0", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", @@ -35,6 +37,7 @@ "angular-svg-round-progressbar": "^9.0.0", "ansi-to-html": "^0.7.2", "base64-js": "^1.5.1", + "buffer": "^6.0.3", "cbor": "npm:@jprochazk/cbor@^0.4.9", "cbor-web": "^8.1.0", "core-js": "^3.21.1", @@ -45,6 +48,7 @@ "jose": "^4.9.0", "js-yaml": "^4.1.0", "marked": "^4.0.0", + "mime": "^4.0.3", "monaco-editor": "^0.33.0", "mustache": "^4.2.0", "ng-qrcode": "^7.0.0", @@ -53,7 +57,7 @@ "pbkdf2": "^3.1.2", "rxjs": "^7.8.1", "swiper": "^8.2.4", - "ts-matches": "^5.2.1", + "ts-matches": "^5.5.1", "tslib": "^2.3.0", "uuid": "^8.3.2", "zone.js": "^0.11.5" @@ -1978,14 +1982,17 @@ "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "isomorphic-fetch": "^3.0.0", - "lodash": "^4.17.21", - "ts-matches": "^5.4.1", + "lodash.merge": "^4.6.2", + "mime": "^4.0.3", + "ts-matches": "^5.5.1", "yaml": "^2.2.2" }, "devDependencies": { "@types/jest": "^29.4.0", - "@types/lodash": "^4.17.5", + "@types/lodash.merge": "^4.6.2", "jest": "^29.4.3", "prettier": "^3.2.5", "ts-jest": "^29.0.5", @@ -5111,6 +5118,28 @@ "@ng-web-apis/common": ">=2.0.0" } }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "devOptional": true, @@ -9954,6 +9983,19 @@ "node": ">=6" } }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/less/node_modules/pify": { "version": "4.0.1", "dev": true, @@ -10551,14 +10593,17 @@ } }, "node_modules/mime": { - "version": "1.6.0", - "dev": true, - "license": "MIT", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.3.tgz", + "integrity": "sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4" + "node": ">=16" } }, "node_modules/mime-db": { @@ -13644,6 +13689,18 @@ "node": ">= 0.8" } }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", "dev": true, @@ -14617,8 +14674,9 @@ } }, "node_modules/ts-matches": { - "version": "v5.2.1", - "license": "MIT" + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.5.1.tgz", + "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" }, "node_modules/ts-morph": { "version": "10.0.2", diff --git a/web/package.json b/web/package.json index 3ea29c6fd..1db0dead4 100644 --- a/web/package.json +++ b/web/package.json @@ -46,6 +46,8 @@ "@ng-web-apis/common": "^2.0.0", "@ng-web-apis/mutation-observer": "^2.0.0", "@ng-web-apis/resize-observer": "^2.0.0", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", @@ -58,6 +60,7 @@ "angular-svg-round-progressbar": "^9.0.0", "ansi-to-html": "^0.7.2", "base64-js": "^1.5.1", + "buffer": "^6.0.3", "cbor": "npm:@jprochazk/cbor@^0.4.9", "cbor-web": "^8.1.0", "core-js": "^3.21.1", @@ -68,6 +71,7 @@ "jose": "^4.9.0", "js-yaml": "^4.1.0", "marked": "^4.0.0", + "mime": "^4.0.3", "monaco-editor": "^0.33.0", "mustache": "^4.2.0", "ng-qrcode": "^7.0.0", @@ -76,7 +80,7 @@ "pbkdf2": "^3.1.2", "rxjs": "^7.8.1", "swiper": "^8.2.4", - "ts-matches": "^5.2.1", + "ts-matches": "^5.5.1", "tslib": "^2.3.0", "uuid": "^8.3.2", "zone.js": "^0.11.5" diff --git a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts index 4026d5183..f6410e748 100644 --- a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts @@ -4,14 +4,15 @@ import { ApiService } from 'src/app/services/api/embassy-api.service' import { ConfigService } from 'src/app/services/config.service' import cbor from 'cbor' import { ErrorToastService } from '@start9labs/shared' -import { T } from '@start9labs/start-sdk' +import { S9pk, T } from '@start9labs/start-sdk' interface Positions { [key: string]: [bigint, bigint] // [position, length] } const MAGIC = new Uint8Array([59, 59]) -const VERSION = new Uint8Array([1]) +const VERSION_1 = new Uint8Array([1]) +const VERSION_2 = new Uint8Array([2]) @Component({ selector: 'sideload', @@ -64,11 +65,36 @@ export class SideloadPage { async validateS9pk(file: File) { const magic = new Uint8Array(await blobToBuffer(file.slice(0, 2))) const version = new Uint8Array(await blobToBuffer(file.slice(2, 3))) - if (compare(magic, MAGIC) && compare(version, VERSION)) { - await this.parseS9pk(file) - return { - invalid: false, - message: 'A valid package file has been detected!', + if (compare(magic, MAGIC)) { + try { + if (compare(version, VERSION_1)) { + await this.parseS9pkV1(file) + return { + invalid: false, + message: 'A valid package file has been detected!', + } + } else if (compare(version, VERSION_2)) { + await this.parseS9pkV2(file) + return { + invalid: false, + message: 'A valid package file has been detected!', + } + } else { + console.error(version) + return { + invalid: true, + message: 'Invalid package file', + } + } + } catch (e) { + console.error(e) + return { + invalid: true, + message: + e instanceof Error + ? `Invalid package file: ${e.message}` + : 'Invalid package file', + } } } else { return { @@ -91,12 +117,9 @@ export class SideloadPage { }) await loader.present() try { - const guid = await this.api.sideloadPackage({ - manifest: this.toUpload.manifest!, - icon: this.toUpload.icon!, - }) + const res = await this.api.sideloadPackage() this.api - .uploadPackage(guid, this.toUpload.file!) + .uploadPackage(res.upload, this.toUpload.file!) .catch(e => console.error(e)) this.navCtrl.navigateRoot('/services') @@ -108,7 +131,7 @@ export class SideloadPage { } } - async parseS9pk(file: File) { + async parseS9pkV1(file: File) { const positions: Positions = {} // magic=2bytes, version=1bytes, pubkey=32bytes, signature=64bytes, toc_length=4bytes = 103byte is starting point let start = 103 @@ -122,6 +145,12 @@ export class SideloadPage { await this.getIcon(positions, file) } + async parseS9pkV2(file: File) { + const s9pk = await S9pk.deserialize(file, null) + this.toUpload.manifest = s9pk.manifest + this.toUpload.icon = await s9pk.icon() + } + async getManifest(positions: Positions, file: Blob) { const data = await blobToBuffer( file.slice( @@ -225,6 +254,7 @@ async function readBlobToArrayBuffer( } function compare(a: Uint8Array, b: Uint8Array) { + if (a.length !== b.length) return false for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false } diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 0c7679754..96be4850b 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -273,7 +273,10 @@ export module RR { manifest: T.Manifest icon: string // base64 } - export type SideloadPacakgeRes = string //guid + export type SideloadPackageRes = { + upload: string // guid + progress: string // guid + } // marketplace diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 1d9c6f805..735b8f1d1 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -243,7 +243,5 @@ export abstract class ApiService { params: RR.DryConfigureDependencyReq, ): Promise - abstract sideloadPackage( - params: RR.SideloadPackageReq, - ): Promise + abstract sideloadPackage(): Promise } diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 057c51013..8cdd0d5e3 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -29,7 +29,7 @@ export class LiveApiService extends ApiService { @Inject(PATCH_CACHE) private readonly cache$: Observable>, ) { super() - ; (window as any).rpcClient = this + ;(window as any).rpcClient = this } // for getting static files: ex icons, instructions, licenses @@ -460,12 +460,10 @@ export class LiveApiService extends ApiService { }) } - async sideloadPackage( - params: RR.SideloadPackageReq, - ): Promise { + async sideloadPackage(): Promise { return this.rpcRequest({ method: 'package.sideload', - params, + params: {}, }) } diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 98efee2ef..0a82ac850 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -1062,11 +1062,12 @@ export class MockApiService extends ApiService { } } - async sideloadPackage( - params: RR.SideloadPackageReq, - ): Promise { + async sideloadPackage(): Promise { await pauseFor(2000) - return '4120e092-05ab-4de2-9fbd-c3f1f4b1df9e' // no significance, randomly generated + return { + upload: '4120e092-05ab-4de2-9fbd-c3f1f4b1df9e', // no significance, randomly generated + progress: '5120e092-05ab-4de2-9fbd-c3f1f4b1df9e', // no significance, randomly generated + } } private async initProgress(): Promise { diff --git a/web/projects/ui/src/polyfills.ts b/web/projects/ui/src/polyfills.ts index a392d45cf..67caa24e8 100644 --- a/web/projects/ui/src/polyfills.ts +++ b/web/projects/ui/src/polyfills.ts @@ -52,8 +52,11 @@ * */ -(window as any).global = window -; (window as any).process = { env: { DEBUG: undefined }, browser: true } +;(window as any).global = window +;(window as any).process = { env: { DEBUG: undefined }, browser: true } + +import { Buffer } from 'buffer' +window.Buffer = Buffer import './zone-flags' From f76e822381c63834cd66aef8682fe1fb48fe56f9 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 10 Jul 2024 11:58:02 -0600 Subject: [PATCH 20/20] port 040 config (#2657) * port 040 config, WIP * update fixtures * use taiga modal for backups too * fix: update Taiga UI and refactor everything to work * chore: package-lock * fix interfaces and mocks for interfaces * better mocks * function to transform old spec to new * delete unused fns * delete unused FE config utils * fix exports from sdk * reorganize exports * functions to translate config * rename unionSelectKey and unionValueKey * Adding in the transformation of the getConfig to the new types. * chore: add Taiga UI to preloader --------- Co-authored-by: waterplea Co-authored-by: Aiden McClelland Co-authored-by: J H --- .../Systems/SystemForEmbassy/index.ts | 17 +- .../SystemForEmbassy/transformConfigSpec.ts | 550 + sdk/lib/StartSdk.ts | 24 - sdk/lib/config/builder/list.ts | 90 - sdk/lib/config/builder/variants.ts | 8 +- sdk/lib/config/configTypes.ts | 186 +- sdk/lib/index.browser.ts | 2 +- sdk/lib/osBindings/InstalledVersionParams.ts | 4 + sdk/lib/osBindings/index.ts | 1 + sdk/lib/test/configBuilder.test.ts | 51 +- sdk/lib/test/configTypes.test.ts | 10 +- sdk/lib/test/output.test.ts | 21 +- sdk/lib/util/getRandomCharInSet.ts | 5 +- sdk/lib/util/getServiceInterface.ts | 4 +- sdk/lib/util/index.browser.ts | 27 +- sdk/lib/util/index.ts | 25 +- sdk/lib/util/typeHelpers.ts | 23 + sdk/scripts/oldSpecToBuilder.ts | 40 +- web/package-lock.json | 9004 +++++++++-------- web/package.json | 18 +- .../setup-wizard/src/app/app.component.ts | 6 +- .../setup-wizard/src/app/app.module.ts | 3 +- .../src/app/pages/attach/attach.page.ts | 12 +- .../src/app/pages/embassy/embassy.page.ts | 8 +- .../src/app/pages/home/home.page.ts | 8 +- .../src/app/pages/loading/loading.page.ts | 13 +- .../src/app/pages/recover/recover.page.ts | 6 +- .../src/app/pages/success/success.page.ts | 9 +- .../src/app/pages/transfer/transfer.page.ts | 6 +- .../shared/assets/img/icon_transparent.png | Bin 0 -> 53685 bytes .../shared/assets/img/storefront-outline.png | Bin 0 -> 10912 bytes .../taiga-ui/icons/tuiIconPaintOutline.svg | 10 + .../components/loading/loading.component.scss | 20 + .../components/loading/loading.component.ts | 17 + .../src/components/loading/loading.module.ts | 13 + .../src/components/loading/loading.service.ts | 10 + .../components/markdown/markdown.component.ts | 2 +- web/projects/shared/src/public-api.ts | 6 +- .../src/services/error-toast.service.ts | 70 - .../shared/src/services/error.service.ts | 43 + web/projects/shared/src/util/invert.ts | 12 + web/projects/ui/src/app/app.module.ts | 4 +- web/projects/ui/src/app/app.providers.ts | 5 + .../app/preloader/preloader.component.html | 42 +- .../app/app/preloader/preloader.component.ts | 34 + .../src/app/app/preloader/preloader.module.ts | 56 +- .../ui/src/app/app/snek/snek.directive.ts | 19 +- .../backup-drives.component.module.ts | 2 - .../backup-drives/backup-drives.component.ts | 146 +- .../form-object/form-label.component.html | 16 - .../form-object/form-object.component.html | 372 - .../form-object.component.module.ts | 47 - .../form-object/form-object.component.scss | 42 - .../form-object/form-object.component.ts | 425 - .../form-object/form-object.pipes.ts | 92 - .../form-object/form-union.component.html | 42 - .../ui/src/app/components/form.component.ts | 167 + .../app/components/form/control.directive.ts | 30 + .../ui/src/app/components/form/control.ts | 35 + .../form/form-array/form-array.component.html | 58 + .../form/form-array/form-array.component.scss | 50 + .../form/form-array/form-array.component.ts | 91 + .../form/form-color/form-color.component.html | 31 + .../form/form-color/form-color.component.scss | 33 + .../form/form-color/form-color.component.ts | 15 + .../form-control/form-control.component.html | 40 + .../form-control/form-control.component.scss | 11 + .../form-control/form-control.component.ts | 71 + .../form-control/form-control.providers.ts | 25 + .../form-datetime.component.html | 43 + .../form-datetime/form-datetime.component.ts | 36 + .../form/form-file/form-file.component.html | 31 + .../form/form-file/form-file.component.scss | 46 + .../form/form-file/form-file.component.ts | 11 + .../form/form-group/form-group.component.html | 30 + .../form/form-group/form-group.component.scss | 35 + .../form/form-group/form-group.component.ts | 35 + .../form/form-group/form-group.providers.ts | 34 + .../form-multiselect.component.html | 18 + .../form-multiselect.component.ts | 49 + .../form-number/form-number.component.html | 18 + .../form/form-number/form-number.component.ts | 11 + .../form-object/form-object.component.html | 25 + .../form-object/form-object.component.scss | 41 + .../form/form-object/form-object.component.ts | 38 + .../form-select/form-select.component.html | 17 + .../form/form-select/form-select.component.ts | 30 + .../form/form-text/form-text.component.html | 44 + .../form/form-text/form-text.component.scss | 8 + .../form/form-text/form-text.component.ts | 16 + .../form-textarea.component.html | 15 + .../form-textarea/form-textarea.component.ts | 12 + .../form-toggle/form-toggle.component.html | 11 + .../form/form-toggle/form-toggle.component.ts | 10 + .../form/form-union/form-union.component.html | 11 + .../form/form-union/form-union.component.scss | 8 + .../form/form-union/form-union.component.ts | 55 + .../ui/src/app/components/form/form.module.ts | 109 + .../ui/src/app/components/form/hint.pipe.ts | 21 + .../app/components/form/invalid.service.ts | 19 + .../src/app/components/form/mustache.pipe.ts | 12 + .../src/app/components/logs/logs.component.ts | 36 +- .../refresh-alert/refresh-alert.component.ts | 13 +- .../update-toast/update-toast.component.ts | 21 +- .../modals/app-config/app-config.module.ts | 21 - .../modals/app-config/app-config.page.html | 144 - .../modals/app-config/app-config.page.scss | 12 - .../app/modals/app-config/app-config.page.ts | 349 - .../app-recover-select.page.ts | 23 +- .../ui/src/app/modals/config-dep.component.ts | 104 + .../ui/src/app/modals/config.component.ts | 281 + .../app/modals/enum-list/enum-list.module.ts | 12 - .../app/modals/enum-list/enum-list.page.html | 45 - .../app/modals/enum-list/enum-list.page.scss | 0 .../app/modals/enum-list/enum-list.page.ts | 50 - .../generic-form/generic-form.module.ts | 19 - .../generic-form/generic-form.page.html | 35 - .../generic-form/generic-form.page.scss | 9 - .../modals/generic-form/generic-form.page.ts | 60 - .../generic-input.component.html | 67 - .../generic-input.component.module.ts | 20 - .../generic-input.component.scss | 0 .../generic-input/generic-input.component.ts | 96 - .../marketplace-settings.module.ts | 17 - .../marketplace-settings.page.html | 95 +- .../marketplace-settings.page.scss | 5 + .../marketplace-settings.page.ts | 354 +- .../registry.component.ts | 42 + .../store-icon.component.ts | 46 + .../app/modals/os-update/os-update.page.ts | 19 +- .../ui/src/app/modals/prompt.component.ts | 123 + .../app-actions/app-actions.module.ts | 2 - .../app-actions/app-actions.page.ts | 60 +- .../app-interfaces/app-interfaces.page.html | 4 +- .../app-interfaces/app-interfaces.page.ts | 97 +- .../app-metrics/app-metrics.page.ts | 6 +- .../app-properties/app-properties.page.ts | 20 +- .../apps-routes/app-show/app-show.module.ts | 3 - .../apps-routes/app-show/app-show.page.ts | 28 +- .../app-show-health-checks.component.html | 2 +- .../app-show-status.component.ts | 68 +- .../app-show/pipes/to-buttons.pipe.ts | 10 +- .../pages/diagnostic-routes/home/home.page.ts | 26 +- .../pages/diagnostic-routes/logs/logs.page.ts | 6 +- .../ui/src/app/pages/init/init.service.ts | 6 +- .../ui/src/app/pages/login/login.page.ts | 16 +- .../marketplace-list.module.ts | 2 - .../marketplace-list/marketplace-list.page.ts | 15 +- .../marketplace-show-controls.component.ts | 30 +- .../pages/notifications/notifications.page.ts | 44 +- .../restore/restore.component.ts | 49 +- .../server-backup/server-backup.page.ts | 73 +- .../server-metrics/server-metrics.page.ts | 8 +- .../server-show/server-show.page.ts | 294 +- .../server-routes/sessions/sessions.page.ts | 23 +- .../server-routes/sideload/sideload.page.ts | 23 +- .../server-routes/ssh-keys/ssh-keys.page.html | 6 +- .../server-routes/ssh-keys/ssh-keys.page.ts | 145 +- .../app/pages/server-routes/wifi/wifi.page.ts | 145 +- .../ui/src/app/pkg-config/config-types.ts | 159 - .../ui/src/app/pkg-config/config-utilities.ts | 235 - .../ui/src/app/services/api/api.fixures.ts | 1221 ++- .../ui/src/app/services/api/api.types.ts | 7 +- .../app/services/api/embassy-api.service.ts | 2 + .../services/api/embassy-live-api.service.ts | 11 +- .../services/api/embassy-mock-api.service.ts | 9 +- .../ui/src/app/services/api/mock-patch.ts | 102 +- .../src/app/services/form-dialog.service.ts | 41 + .../ui/src/app/services/form.service.ts | 487 +- .../ui/src/app/services/modal.service.ts | 24 - .../ui/src/app/util/configBuilderToSpec.ts | 9 + web/projects/ui/src/index.html | 28 +- web/projects/ui/src/styles.scss | 13 +- 173 files changed, 9761 insertions(+), 9200 deletions(-) create mode 100644 container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts create mode 100644 sdk/lib/osBindings/InstalledVersionParams.ts create mode 100644 sdk/lib/util/typeHelpers.ts create mode 100644 web/projects/shared/assets/img/icon_transparent.png create mode 100644 web/projects/shared/assets/img/storefront-outline.png create mode 100644 web/projects/shared/assets/taiga-ui/icons/tuiIconPaintOutline.svg create mode 100644 web/projects/shared/src/components/loading/loading.component.scss create mode 100644 web/projects/shared/src/components/loading/loading.component.ts create mode 100644 web/projects/shared/src/components/loading/loading.module.ts create mode 100644 web/projects/shared/src/components/loading/loading.service.ts delete mode 100644 web/projects/shared/src/services/error-toast.service.ts create mode 100644 web/projects/shared/src/services/error.service.ts create mode 100644 web/projects/shared/src/util/invert.ts delete mode 100644 web/projects/ui/src/app/components/form-object/form-label.component.html delete mode 100644 web/projects/ui/src/app/components/form-object/form-object.component.html delete mode 100644 web/projects/ui/src/app/components/form-object/form-object.component.module.ts delete mode 100644 web/projects/ui/src/app/components/form-object/form-object.component.scss delete mode 100644 web/projects/ui/src/app/components/form-object/form-object.component.ts delete mode 100644 web/projects/ui/src/app/components/form-object/form-object.pipes.ts delete mode 100644 web/projects/ui/src/app/components/form-object/form-union.component.html create mode 100644 web/projects/ui/src/app/components/form.component.ts create mode 100644 web/projects/ui/src/app/components/form/control.directive.ts create mode 100644 web/projects/ui/src/app/components/form/control.ts create mode 100644 web/projects/ui/src/app/components/form/form-array/form-array.component.html create mode 100644 web/projects/ui/src/app/components/form/form-array/form-array.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-array/form-array.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-color/form-color.component.html create mode 100644 web/projects/ui/src/app/components/form/form-color/form-color.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-color/form-color.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-control/form-control.component.html create mode 100644 web/projects/ui/src/app/components/form/form-control/form-control.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-control/form-control.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-control/form-control.providers.ts create mode 100644 web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.html create mode 100644 web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-file/form-file.component.html create mode 100644 web/projects/ui/src/app/components/form/form-file/form-file.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-file/form-file.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-group/form-group.component.html create mode 100644 web/projects/ui/src/app/components/form/form-group/form-group.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-group/form-group.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-group/form-group.providers.ts create mode 100644 web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.html create mode 100644 web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-number/form-number.component.html create mode 100644 web/projects/ui/src/app/components/form/form-number/form-number.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-object/form-object.component.html create mode 100644 web/projects/ui/src/app/components/form/form-object/form-object.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-object/form-object.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-select/form-select.component.html create mode 100644 web/projects/ui/src/app/components/form/form-select/form-select.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-text/form-text.component.html create mode 100644 web/projects/ui/src/app/components/form/form-text/form-text.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-text/form-text.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.html create mode 100644 web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.html create mode 100644 web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts create mode 100644 web/projects/ui/src/app/components/form/form-union/form-union.component.html create mode 100644 web/projects/ui/src/app/components/form/form-union/form-union.component.scss create mode 100644 web/projects/ui/src/app/components/form/form-union/form-union.component.ts create mode 100644 web/projects/ui/src/app/components/form/form.module.ts create mode 100644 web/projects/ui/src/app/components/form/hint.pipe.ts create mode 100644 web/projects/ui/src/app/components/form/invalid.service.ts create mode 100644 web/projects/ui/src/app/components/form/mustache.pipe.ts delete mode 100644 web/projects/ui/src/app/modals/app-config/app-config.module.ts delete mode 100644 web/projects/ui/src/app/modals/app-config/app-config.page.html delete mode 100644 web/projects/ui/src/app/modals/app-config/app-config.page.scss delete mode 100644 web/projects/ui/src/app/modals/app-config/app-config.page.ts create mode 100644 web/projects/ui/src/app/modals/config-dep.component.ts create mode 100644 web/projects/ui/src/app/modals/config.component.ts delete mode 100644 web/projects/ui/src/app/modals/enum-list/enum-list.module.ts delete mode 100644 web/projects/ui/src/app/modals/enum-list/enum-list.page.html delete mode 100644 web/projects/ui/src/app/modals/enum-list/enum-list.page.scss delete mode 100644 web/projects/ui/src/app/modals/enum-list/enum-list.page.ts delete mode 100644 web/projects/ui/src/app/modals/generic-form/generic-form.module.ts delete mode 100644 web/projects/ui/src/app/modals/generic-form/generic-form.page.html delete mode 100644 web/projects/ui/src/app/modals/generic-form/generic-form.page.scss delete mode 100644 web/projects/ui/src/app/modals/generic-form/generic-form.page.ts delete mode 100644 web/projects/ui/src/app/modals/generic-input/generic-input.component.html delete mode 100644 web/projects/ui/src/app/modals/generic-input/generic-input.component.module.ts delete mode 100644 web/projects/ui/src/app/modals/generic-input/generic-input.component.scss delete mode 100644 web/projects/ui/src/app/modals/generic-input/generic-input.component.ts delete mode 100644 web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts create mode 100644 web/projects/ui/src/app/modals/marketplace-settings/registry.component.ts create mode 100644 web/projects/ui/src/app/modals/marketplace-settings/store-icon.component.ts create mode 100644 web/projects/ui/src/app/modals/prompt.component.ts delete mode 100644 web/projects/ui/src/app/pkg-config/config-types.ts delete mode 100644 web/projects/ui/src/app/pkg-config/config-utilities.ts create mode 100644 web/projects/ui/src/app/services/form-dialog.service.ts delete mode 100644 web/projects/ui/src/app/services/modal.service.ts create mode 100644 web/projects/ui/src/app/util/configBuilderToSpec.ts diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 127fb0e09..3a8d4f4b2 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -42,6 +42,12 @@ import { } from "@start9labs/start-sdk/cjs/lib/interfaces/Host" import { ServiceInterfaceBuilder } from "@start9labs/start-sdk/cjs/lib/interfaces/ServiceInterfaceBuilder" import { Effects } from "../../../Models/Effects" +import { + OldConfigSpec, + matchOldConfigSpec, + transformConfigSpec, + transformOldConfigToNew, +} from "./transformConfigSpec" type Optional = A | undefined | null function todo(): never { @@ -533,7 +539,9 @@ export class SystemForEmbassy implements System { effects: Effects, timeoutMs: number | null, ): Promise { - return this.getConfigUncleaned(effects, timeoutMs).then(removePointers) + return this.getConfigUncleaned(effects, timeoutMs) + .then(removePointers) + .then(convertToNewConfig) } private async getConfigUncleaned( effects: Effects, @@ -1054,3 +1062,10 @@ function extractServiceInterfaceId(manifest: Manifest, specInterface: string) { const serviceInterfaceId = `${specInterface}-${internalPort}` return serviceInterfaceId } +async function convertToNewConfig(value: T.ConfigRes): Promise { + const valueSpec: OldConfigSpec = matchOldConfigSpec.unsafeCast(value.spec) + const spec = transformConfigSpec(valueSpec) + if (!value.config) return { spec, config: null } + const config = transformOldConfigToNew(valueSpec, value.config) + return { spec, config } +} diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts new file mode 100644 index 000000000..cb7809903 --- /dev/null +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/transformConfigSpec.ts @@ -0,0 +1,550 @@ +import { CT } from "@start9labs/start-sdk" +import { + dictionary, + object, + anyOf, + string, + literals, + array, + number, + boolean, + Parser, + deferred, + every, + nill, +} from "ts-matches" + +export function transformConfigSpec(oldSpec: OldConfigSpec): CT.InputSpec { + return Object.entries(oldSpec).reduce((inputSpec, [key, oldVal]) => { + let newVal: CT.ValueSpec + + if (oldVal.type === "boolean") { + newVal = { + type: "toggle", + name: oldVal.name, + default: oldVal.default, + description: oldVal.description || null, + warning: oldVal.warning || null, + disabled: false, + immutable: false, + } + } else if (oldVal.type === "enum") { + newVal = { + type: "select", + name: oldVal.name, + description: oldVal.description || null, + warning: oldVal.warning || null, + default: oldVal.default, + values: oldVal.values.reduce( + (obj, curr) => ({ + ...obj, + [curr]: oldVal["value-names"][curr], + }), + {}, + ), + required: false, + disabled: false, + immutable: false, + } + } else if (oldVal.type === "list") { + newVal = getListSpec(oldVal) + } else if (oldVal.type === "number") { + const range = Range.from(oldVal.range) + + newVal = { + type: "number", + name: oldVal.name, + default: oldVal.default || null, + description: oldVal.description || null, + warning: oldVal.warning || null, + disabled: false, + immutable: false, + required: !oldVal.nullable, + min: range.min + ? range.minInclusive + ? range.min + : range.min + 1 + : null, + max: range.max + ? range.maxInclusive + ? range.max + : range.max - 1 + : null, + integer: oldVal.integral, + step: null, + units: oldVal.units || null, + placeholder: oldVal.placeholder || null, + } + } else if (oldVal.type === "object") { + newVal = { + type: "object", + name: oldVal.name, + description: oldVal.description || null, + warning: oldVal.warning || null, + spec: transformConfigSpec(matchOldConfigSpec.unsafeCast(oldVal.spec)), + } + } else if (oldVal.type === "string") { + newVal = { + type: "text", + name: oldVal.name, + default: oldVal.default || null, + description: oldVal.description || null, + warning: oldVal.warning || null, + disabled: false, + immutable: false, + required: !oldVal.nullable, + patterns: + oldVal.pattern && oldVal["pattern-description"] + ? [ + { + regex: oldVal.pattern, + description: oldVal["pattern-description"], + }, + ] + : [], + minLength: null, + maxLength: null, + masked: oldVal.masked, + generate: null, + inputmode: "text", + placeholder: oldVal.placeholder || null, + } + } else { + newVal = { + type: "union", + name: oldVal.tag.name, + description: oldVal.tag.description || null, + warning: oldVal.tag.warning || null, + variants: Object.entries(oldVal.variants).reduce( + (obj, [id, spec]) => ({ + ...obj, + [id]: { + name: oldVal.tag["variant-names"][id], + spec: transformConfigSpec(matchOldConfigSpec.unsafeCast(spec)), + }, + }), + {} as Record, + ), + disabled: false, + required: true, + default: oldVal.default, + immutable: false, + } + } + + return { + ...inputSpec, + [key]: newVal, + } + }, {} as CT.InputSpec) +} + +export function transformOldConfigToNew( + spec: OldConfigSpec, + config: Record, +): Record { + return Object.entries(spec).reduce((obj, [key, val]) => { + let newVal = config[key] + + if (isObject(val)) { + newVal = transformOldConfigToNew( + matchOldConfigSpec.unsafeCast(val.spec), + config[key], + ) + } + + if (isUnion(val)) { + const selection = config[key][val.tag.id] + delete config[key][val.tag.id] + + newVal = { + selection, + value: transformOldConfigToNew( + matchOldConfigSpec.unsafeCast(val.variants[selection]), + config[key], + ), + } + } + + if (isList(val) && isObjectList(val)) { + newVal = (config[key] as object[]).map((obj) => + transformOldConfigToNew( + matchOldConfigSpec.unsafeCast(val.spec.spec), + obj, + ), + ) + } + + return { + ...obj, + [key]: newVal, + } + }, {}) +} + +export function transformNewConfigToOld( + spec: OldConfigSpec, + config: Record, +): Record { + return Object.entries(spec).reduce((obj, [key, val]) => { + let newVal = config[key] + + if (isObject(val)) { + newVal = transformNewConfigToOld( + matchOldConfigSpec.unsafeCast(val.spec), + config[key], + ) + } + + if (isUnion(val)) { + newVal = { + [val.tag.id]: config[key].selection, + ...transformNewConfigToOld( + matchOldConfigSpec.unsafeCast(val.variants[config[key].selection]), + config[key].unionSelectValue, + ), + } + } + + if (isList(val) && isObjectList(val)) { + newVal = (config[key] as object[]).map((obj) => + transformNewConfigToOld( + matchOldConfigSpec.unsafeCast(val.spec.spec), + obj, + ), + ) + } + + return { + ...obj, + [key]: newVal, + } + }, {}) +} + +function getListSpec( + oldVal: OldValueSpecList, +): CT.ValueSpecMultiselect | CT.ValueSpecList { + const range = Range.from(oldVal.range) + + let partial: Omit = { + name: oldVal.name, + description: oldVal.description || null, + warning: oldVal.warning || null, + minLength: range.min + ? range.minInclusive + ? range.min + : range.min + 1 + : null, + maxLength: range.max + ? range.maxInclusive + ? range.max + : range.max - 1 + : null, + disabled: false, + } + + if (isEnumList(oldVal)) { + return { + ...partial, + type: "multiselect", + default: oldVal.default as string[], + immutable: false, + values: oldVal.spec.values.reduce( + (obj, curr) => ({ + ...obj, + [curr]: oldVal.spec["value-names"][curr], + }), + {}, + ), + } + } else if (isStringList(oldVal)) { + return { + ...partial, + type: "list", + default: oldVal.default as string[], + spec: { + type: "text", + patterns: + oldVal.spec.pattern && oldVal.spec["pattern-description"] + ? [ + { + regex: oldVal.spec.pattern, + description: oldVal.spec["pattern-description"], + }, + ] + : [], + minLength: null, + maxLength: null, + masked: oldVal.spec.masked, + generate: null, + inputmode: "text", + placeholder: oldVal.spec.placeholder || null, + }, + } + } else if (isObjectList(oldVal)) { + return { + ...partial, + type: "list", + default: oldVal.default as Record[], + spec: { + type: "object", + spec: transformConfigSpec( + matchOldConfigSpec.unsafeCast(oldVal.spec.spec), + ), + uniqueBy: oldVal.spec["unique-by"], + displayAs: oldVal.spec["display-as"] || null, + }, + } + } else { + throw new Error("Invalid list subtype. enum, string, and object permitted.") + } +} + +function isObject(val: OldValueSpec): val is OldValueSpecObject { + return val.type === "object" +} + +function isUnion(val: OldValueSpec): val is OldValueSpecUnion { + return val.type === "union" +} + +function isList(val: OldValueSpec): val is OldValueSpecList { + return val.type === "list" +} + +function isEnumList( + val: OldValueSpecList, +): val is OldValueSpecList & { subtype: "enum" } { + return val.subtype === "enum" +} + +function isStringList( + val: OldValueSpecList, +): val is OldValueSpecList & { subtype: "string" } { + return val.subtype === "string" +} + +function isObjectList( + val: OldValueSpecList, +): val is OldValueSpecList & { subtype: "object" } { + if (["number", "union"].includes(val.subtype)) { + throw new Error("Invalid list subtype. enum, string, and object permitted.") + } + return val.subtype === "object" +} +export type OldConfigSpec = Record +const [_matchOldConfigSpec, setMatchOldConfigSpec] = deferred() +export const matchOldConfigSpec = _matchOldConfigSpec as Parser< + unknown, + OldConfigSpec +> +export const matchOldDefaultString = anyOf( + string, + object({ charset: string, len: number }), +) +type OldDefaultString = typeof matchOldDefaultString._TYPE + +export const matchOldValueSpecString = object( + { + masked: boolean, + copyable: boolean, + type: literals("string"), + nullable: boolean, + name: string, + placeholder: string, + pattern: string, + "pattern-description": string, + default: matchOldDefaultString, + textarea: boolean, + description: string, + warning: string, + }, + [ + "placeholder", + "pattern", + "pattern-description", + "default", + "textarea", + "description", + "warning", + ], +) + +export const matchOldValueSpecNumber = object( + { + type: literals("number"), + nullable: boolean, + name: string, + range: string, + integral: boolean, + default: number, + description: string, + warning: string, + units: string, + placeholder: string, + }, + ["default", "description", "warning", "units", "placeholder"], +) +type OldValueSpecNumber = typeof matchOldValueSpecNumber._TYPE + +export const matchOldValueSpecBoolean = object( + { + type: literals("boolean"), + default: boolean, + name: string, + description: string, + warning: string, + }, + ["description", "warning"], +) +type OldValueSpecBoolean = typeof matchOldValueSpecBoolean._TYPE + +const matchOldValueSpecObject = object( + { + type: literals("object"), + spec: _matchOldConfigSpec, + name: string, + description: string, + warning: string, + }, + ["description", "warning"], +) +type OldValueSpecObject = typeof matchOldValueSpecObject._TYPE + +const matchOldValueSpecEnum = object( + { + values: array(string), + "value-names": dictionary([string, string]), + type: literals("enum"), + default: string, + name: string, + description: string, + warning: string, + }, + ["description", "warning"], +) +type OldValueSpecEnum = typeof matchOldValueSpecEnum._TYPE + +const matchOldUnionTagSpec = object( + { + id: string, // The name of the field containing one of the union variants + "variant-names": dictionary([string, string]), // The name of each variant + name: string, + description: string, + warning: string, + }, + ["description", "warning"], +) +const matchOldValueSpecUnion = object({ + type: literals("union"), + tag: matchOldUnionTagSpec, + variants: dictionary([string, _matchOldConfigSpec]), + default: string, +}) +type OldValueSpecUnion = typeof matchOldValueSpecUnion._TYPE + +const [matchOldUniqueBy, setOldUniqueBy] = deferred() +type OldUniqueBy = + | null + | string + | { any: OldUniqueBy[] } + | { all: OldUniqueBy[] } + +setOldUniqueBy( + anyOf( + nill, + string, + object({ any: array(matchOldUniqueBy) }), + object({ all: array(matchOldUniqueBy) }), + ), +) + +const matchOldListValueSpecObject = object( + { + spec: _matchOldConfigSpec, // this is a mapped type of the config object at this level, replacing the object's values with specs on those values + "unique-by": matchOldUniqueBy, // indicates whether duplicates can be permitted in the list + "display-as": string, // this should be a handlebars template which can make use of the entire config which corresponds to 'spec' + }, + ["display-as"], +) +const matchOldListValueSpecString = object( + { + masked: boolean, + copyable: boolean, + pattern: string, + "pattern-description": string, + placeholder: string, + }, + ["pattern", "pattern-description", "placeholder"], +) + +const matchOldListValueSpecEnum = object({ + values: array(string), + "value-names": dictionary([string, string]), +}) + +// represents a spec for a list +const matchOldValueSpecList = every( + object( + { + type: literals("list"), + range: string, // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules + default: anyOf( + array(string), + array(number), + array(matchOldDefaultString), + array(object), + ), + name: string, + description: string, + warning: string, + }, + ["description", "warning"], + ), + anyOf( + object({ + subtype: literals("string"), + spec: matchOldListValueSpecString, + }), + object({ + subtype: literals("enum"), + spec: matchOldListValueSpecEnum, + }), + object({ + subtype: literals("object"), + spec: matchOldListValueSpecObject, + }), + ), +) +type OldValueSpecList = typeof matchOldValueSpecList._TYPE + +export const matchOldValueSpec = anyOf( + matchOldValueSpecString, + matchOldValueSpecNumber, + matchOldValueSpecBoolean, + matchOldValueSpecObject, + matchOldValueSpecEnum, + matchOldValueSpecList, + matchOldValueSpecUnion, +) +type OldValueSpec = typeof matchOldValueSpec._TYPE + +setMatchOldConfigSpec(dictionary([string, matchOldValueSpec])) + +export class Range { + min?: number + max?: number + minInclusive!: boolean + maxInclusive!: boolean + + static from(s: string = "(*,*)"): Range { + const r = new Range() + r.minInclusive = s.startsWith("[") + r.maxInclusive = s.endsWith("]") + const [minStr, maxStr] = s.split(",").map((a) => a.trim()) + r.min = minStr === "(*" ? undefined : Number(minStr.slice(1)) + r.max = maxStr === "*)" ? undefined : Number(maxStr.slice(0, -1)) + return r + } +} diff --git a/sdk/lib/StartSdk.ts b/sdk/lib/StartSdk.ts index 4208928c9..fac78a4af 100644 --- a/sdk/lib/StartSdk.ts +++ b/sdk/lib/StartSdk.ts @@ -480,7 +480,6 @@ export class StartSdk { }, List: { text: List.text, - number: List.number, obj: >( a: { name: string @@ -523,29 +522,6 @@ export class StartSdk { } >, ) => List.dynamicText(getA), - dynamicNumber: ( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - /** Default = [] */ - default?: string[] - minLength?: number | null - maxLength?: number | null - disabled?: false | string - spec: { - integer: boolean - min?: number | null - max?: number | null - step?: number | null - units?: string | null - placeholder?: string | null - } - } - >, - ) => List.dynamicNumber(getA), }, Migration: { of: (options: { diff --git a/sdk/lib/config/builder/list.ts b/sdk/lib/config/builder/list.ts index 8a251069d..f230b8608 100644 --- a/sdk/lib/config/builder/list.ts +++ b/sdk/lib/config/builder/list.ts @@ -125,96 +125,6 @@ export class List { return built }, arrayOf(string)) } - static number( - a: { - name: string - description?: string | null - warning?: string | null - /** Default = [] */ - default?: string[] - minLength?: number | null - maxLength?: number | null - }, - aSpec: { - integer: boolean - min?: number | null - max?: number | null - step?: number | null - units?: string | null - placeholder?: string | null - }, - ) { - return new List(() => { - const spec = { - type: "number" as const, - placeholder: null, - min: null, - max: null, - step: null, - units: null, - ...aSpec, - } - const built: ValueSpecListOf<"number"> = { - description: null, - warning: null, - minLength: null, - maxLength: null, - default: [], - type: "list" as const, - disabled: false, - ...a, - spec, - } - return built - }, arrayOf(number)) - } - static dynamicNumber( - getA: LazyBuild< - Store, - { - name: string - description?: string | null - warning?: string | null - /** Default = [] */ - default?: string[] - minLength?: number | null - maxLength?: number | null - disabled?: false | string - spec: { - integer: boolean - min?: number | null - max?: number | null - step?: number | null - units?: string | null - placeholder?: string | null - } - } - >, - ) { - return new List(async (options) => { - const { spec: aSpec, ...a } = await getA(options) - const spec = { - type: "number" as const, - placeholder: null, - min: null, - max: null, - step: null, - units: null, - ...aSpec, - } - return { - description: null, - warning: null, - minLength: null, - maxLength: null, - default: [], - type: "list" as const, - disabled: false, - ...a, - spec, - } - }, arrayOf(number)) - } static obj, Store>( a: { name: string diff --git a/sdk/lib/config/builder/variants.ts b/sdk/lib/config/builder/variants.ts index 1e7a2a384..352f16828 100644 --- a/sdk/lib/config/builder/variants.ts +++ b/sdk/lib/config/builder/variants.ts @@ -69,8 +69,8 @@ export class Variants { const validator = anyOf( ...Object.entries(a).map(([name, { spec }]) => object({ - unionSelectKey: literals(name), - unionValueKey: spec.validator, + selection: literals(name), + value: spec.validator, }), ), ) as Parser @@ -78,9 +78,9 @@ export class Variants { return new Variants< { [K in keyof VariantValues]: { - unionSelectKey: K + selection: K // prettier-ignore - unionValueKey: + value: VariantValues[K]["spec"] extends (Config | Config) ? B : never } diff --git a/sdk/lib/config/configTypes.ts b/sdk/lib/config/configTypes.ts index 14e0e1d1d..14d857433 100644 --- a/sdk/lib/config/configTypes.ts +++ b/sdk/lib/config/configTypes.ts @@ -15,70 +15,93 @@ export type ValueType = export type ValueSpec = ValueSpecOf /** core spec types. These types provide the metadata for performing validations */ // prettier-ignore -export type ValueSpecOf = T extends "text" - ? ValueSpecText - : T extends "textarea" - ? ValueSpecTextarea - : T extends "number" - ? ValueSpecNumber - : T extends "color" - ? ValueSpecColor - : T extends "datetime" - ? ValueSpecDatetime - : T extends "toggle" - ? ValueSpecToggle - : T extends "select" - ? ValueSpecSelect - : T extends "multiselect" - ? ValueSpecMultiselect - : T extends "list" - ? ValueSpecList - : T extends "object" - ? ValueSpecObject - : T extends "file" - ? ValueSpecFile - : T extends "union" - ? ValueSpecUnion - : never +export type ValueSpecOf = + T extends "text" ? ValueSpecText : + T extends "textarea" ? ValueSpecTextarea : + T extends "number" ? ValueSpecNumber : + T extends "color" ? ValueSpecColor : + T extends "datetime" ? ValueSpecDatetime : + T extends "toggle" ? ValueSpecToggle : + T extends "select" ? ValueSpecSelect : + T extends "multiselect" ? ValueSpecMultiselect : + T extends "list" ? ValueSpecList : + T extends "object" ? ValueSpecObject : + T extends "file" ? ValueSpecFile : + T extends "union" ? ValueSpecUnion : + never + +export type ValueSpecText = { + name: string + description: string | null + warning: string | null + + type: "text" + patterns: Pattern[] + minLength: number | null + maxLength: number | null + masked: boolean + + inputmode: "text" | "email" | "tel" | "url" + placeholder: string | null -export interface ValueSpecText extends ListValueSpecText, WithStandalone { required: boolean default: DefaultString | null disabled: false | string generate: null | RandomString - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecTextarea extends WithStandalone { +export type ValueSpecTextarea = { + name: string + description: string | null + warning: string | null + type: "textarea" placeholder: string | null minLength: number | null maxLength: number | null required: boolean disabled: false | string - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } export type FilePath = { filePath: string } -export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone { +export type ValueSpecNumber = { + type: "number" + min: number | null + max: number | null + integer: boolean + step: number | null + units: string | null + placeholder: string | null + name: string + description: string | null + warning: string | null required: boolean default: number | null disabled: false | string - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecColor extends WithStandalone { +export type ValueSpecColor = { + name: string + description: string | null + warning: string | null + type: "color" required: boolean default: string | null disabled: false | string - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecDatetime extends WithStandalone { +export type ValueSpecDatetime = { + name: string + description: string | null + warning: string | null type: "datetime" required: boolean inputmode: "date" | "time" | "datetime-local" @@ -86,10 +109,14 @@ export interface ValueSpecDatetime extends WithStandalone { max: string | null default: string | null disabled: false | string - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecSelect extends SelectBase, WithStandalone { +export type ValueSpecSelect = { + values: Record + name: string + description: string | null + warning: string | null type: "select" required: boolean default: string | null @@ -99,10 +126,16 @@ export interface ValueSpecSelect extends SelectBase, WithStandalone { * string[] means that the options are disabled */ disabled: false | string | string[] - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecMultiselect extends SelectBase, WithStandalone { +export type ValueSpecMultiselect = { + values: Record + + name: string + description: string | null + warning: string | null + type: "multiselect" minLength: number | null maxLength: number | null @@ -113,17 +146,25 @@ export interface ValueSpecMultiselect extends SelectBase, WithStandalone { */ disabled: false | string | string[] default: string[] - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecToggle extends WithStandalone { +export type ValueSpecToggle = { + name: string + description: string | null + warning: string | null + type: "toggle" default: boolean | null disabled: false | string - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecUnion extends WithStandalone { +export type ValueSpecUnion = { + name: string + description: string | null + warning: string | null + type: "union" variants: Record< string, @@ -140,39 +181,37 @@ export interface ValueSpecUnion extends WithStandalone { disabled: false | string | string[] required: boolean default: string | null - /** Immutable means it can only be configed at the first config then never again */ + /** Immutable means it can only be configured at the first config then never again */ immutable: boolean } -export interface ValueSpecFile extends WithStandalone { +export type ValueSpecFile = { + name: string + description: string | null + warning: string | null type: "file" extensions: string[] required: boolean } -export interface ValueSpecObject extends WithStandalone { - type: "object" - spec: InputSpec -} -export interface WithStandalone { +export type ValueSpecObject = { name: string description: string | null warning: string | null + type: "object" + spec: InputSpec } -export interface SelectBase { - values: Record -} -export type ListValueSpecType = "text" | "number" | "object" +export type ListValueSpecType = "text" | "object" /** represents a spec for the values of a list */ -export type ListValueSpecOf = T extends "text" - ? ListValueSpecText - : T extends "number" - ? ListValueSpecNumber - : T extends "object" - ? ListValueSpecObject - : never +// prettier-ignore +export type ListValueSpecOf = + T extends "text" ? ListValueSpecText : + T extends "object" ? ListValueSpecObject : + never /** represents a spec for a list */ export type ValueSpecList = ValueSpecListOf -export interface ValueSpecListOf - extends WithStandalone { +export type ValueSpecListOf = { + name: string + description: string | null + warning: string | null type: "list" spec: ListValueSpecOf minLength: number | null @@ -180,19 +219,17 @@ export interface ValueSpecListOf disabled: false | string default: | string[] - | number[] | DefaultString[] | Record[] | readonly string[] - | readonly number[] | readonly DefaultString[] | readonly Record[] } -export interface Pattern { +export type Pattern = { regex: string description: string } -export interface ListValueSpecText { +export type ListValueSpecText = { type: "text" patterns: Pattern[] minLength: number | null @@ -203,16 +240,8 @@ export interface ListValueSpecText { inputmode: "text" | "email" | "tel" | "url" placeholder: string | null } -export interface ListValueSpecNumber { - type: "number" - min: number | null - max: number | null - integer: boolean - step: number | null - units: string | null - placeholder: string | null -} -export interface ListValueSpecObject { + +export type ListValueSpecObject = { type: "object" /** this is a mapped type of the config object at this level, replacing the object's values with specs on those values */ spec: InputSpec @@ -242,8 +271,3 @@ export function isValueSpecListOf( ): t is ValueSpecListOf & { spec: ListValueSpecOf } { return "spec" in t && t.spec.type === s } -export const unionSelectKey = "unionSelectKey" as const -export type UnionSelectKey = typeof unionSelectKey - -export const unionValueKey = "unionValueKey" as const -export type UnionValueKey = typeof unionValueKey diff --git a/sdk/lib/index.browser.ts b/sdk/lib/index.browser.ts index 075bfcf50..c7ab45e60 100644 --- a/sdk/lib/index.browser.ts +++ b/sdk/lib/index.browser.ts @@ -12,4 +12,4 @@ export * as T from "./types" export * as yaml from "yaml" export * as matches from "ts-matches" -export * as util from "./util/index.browser" +export * as utils from "./util/index.browser" diff --git a/sdk/lib/osBindings/InstalledVersionParams.ts b/sdk/lib/osBindings/InstalledVersionParams.ts new file mode 100644 index 000000000..637f9b6ce --- /dev/null +++ b/sdk/lib/osBindings/InstalledVersionParams.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PackageId } from "./PackageId" + +export type InstalledVersionParams = { id: PackageId } diff --git a/sdk/lib/osBindings/index.ts b/sdk/lib/osBindings/index.ts index 22b03a7ca..32e57956a 100644 --- a/sdk/lib/osBindings/index.ts +++ b/sdk/lib/osBindings/index.ts @@ -79,6 +79,7 @@ export { ImageMetadata } from "./ImageMetadata" export { ImageSource } from "./ImageSource" export { InitProgressRes } from "./InitProgressRes" export { InstalledState } from "./InstalledState" +export { InstalledVersionParams } from "./InstalledVersionParams" export { InstallingInfo } from "./InstallingInfo" export { InstallingState } from "./InstallingState" export { IpHostname } from "./IpHostname" diff --git a/sdk/lib/test/configBuilder.test.ts b/sdk/lib/test/configBuilder.test.ts index 9738475a6..c5003c55b 100644 --- a/sdk/lib/test/configBuilder.test.ts +++ b/sdk/lib/test/configBuilder.test.ts @@ -268,26 +268,9 @@ describe("values", () => { }), ) const validator = value.validator - validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } }) + validator.unsafeCast({ selection: "a", value: { b: false } }) type Test = typeof validator._TYPE - testOutput()( - null, - ) - }) - test("list", async () => { - const value = Value.list( - List.number( - { - name: "test", - }, - { - integer: false, - }, - ), - ) - const validator = value.validator - validator.unsafeCast([1, 2, 3]) - testOutput()(null) + testOutput()(null) }) describe("dynamic", () => { @@ -577,12 +560,12 @@ describe("values", () => { }), ) const validator = value.validator - validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } }) + validator.unsafeCast({ selection: "a", value: { b: false } }) type Test = typeof validator._TYPE testOutput< Test, - | { unionSelectKey: "a"; unionValueKey: { b: boolean } } - | { unionSelectKey: "b"; unionValueKey: { b: boolean } } + | { selection: "a"; value: { b: boolean } } + | { selection: "b"; value: { b: boolean } } >()(null) const built = await value.build({} as any) @@ -644,12 +627,12 @@ describe("values", () => { }), ) const validator = value.validator - validator.unsafeCast({ unionSelectKey: "a", unionValueKey: { b: false } }) + validator.unsafeCast({ selection: "a", value: { b: false } }) type Test = typeof validator._TYPE testOutput< Test, - | { unionSelectKey: "a"; unionValueKey: { b: boolean } } - | { unionSelectKey: "b"; unionValueKey: { b: boolean } } + | { selection: "a"; value: { b: boolean } } + | { selection: "b"; value: { b: boolean } } | null | undefined >()(null) @@ -736,24 +719,6 @@ describe("Builder List", () => { }) }) }) - test("number", async () => { - const value = Value.list( - List.dynamicNumber(() => ({ - name: "test", - spec: { integer: true }, - })), - ) - const validator = value.validator - expect(() => validator.unsafeCast(["test", "text"])).toThrowError() - validator.unsafeCast([4, 2]) - expect(() => validator.unsafeCast(null)).toThrowError() - validator.unsafeCast([]) - testOutput()(null) - expect(await value.build({} as any)).toMatchObject({ - name: "test", - spec: { integer: true }, - }) - }) }) describe("Nested nullable values", () => { diff --git a/sdk/lib/test/configTypes.test.ts b/sdk/lib/test/configTypes.test.ts index 7e3ff5ca6..6d0b9a3d8 100644 --- a/sdk/lib/test/configTypes.test.ts +++ b/sdk/lib/test/configTypes.test.ts @@ -1,15 +1,11 @@ -import { - ListValueSpecOf, - ValueSpec, - isValueSpecListOf, -} from "../config/configTypes" +import { ListValueSpecOf, isValueSpecListOf } from "../config/configTypes" import { Config } from "../config/builder/config" import { List } from "../config/builder/list" import { Value } from "../config/builder/value" describe("Config Types", () => { test("isValueSpecListOf", async () => { - const options = [List.obj, List.text, List.number] + const options = [List.obj, List.text] for (const option of options) { const test = (option as any)( {} as any, @@ -18,8 +14,6 @@ describe("Config Types", () => { const someList = await Value.list(test).build({} as any) if (isValueSpecListOf(someList, "text")) { someList.spec satisfies ListValueSpecOf<"text"> - } else if (isValueSpecListOf(someList, "number")) { - someList.spec satisfies ListValueSpecOf<"number"> } else if (isValueSpecListOf(someList, "object")) { someList.spec satisfies ListValueSpecOf<"object"> } else { diff --git a/sdk/lib/test/output.test.ts b/sdk/lib/test/output.test.ts index 2b3afb5de..1df84f3af 100644 --- a/sdk/lib/test/output.test.ts +++ b/sdk/lib/test/output.test.ts @@ -1,9 +1,3 @@ -import { - UnionSelectKey, - unionSelectKey, - UnionValueKey, - unionValueKey, -} from "../config/configTypes" import { ConfigSpec, matchConfigSpec } from "./output" import * as _I from "../index" import { camelCase } from "../../scripts/oldSpecToBuilder" @@ -30,13 +24,10 @@ testOutput< ConfigSpec["advanced"]["peers"]["addnode"][0]["hostname"], string | null | undefined >()(null) -testOutput< - ConfigSpec["testListUnion"][0]["union"][UnionValueKey]["name"], - string ->()(null) -testOutput()( +testOutput()( null, ) +testOutput()(null) testOutput>()( null, ) @@ -45,7 +36,7 @@ testOutput>()( testOutput()(null) // prettier-ignore // @ts-expect-error Expect that the string is the one above -testOutput()(null); +testOutput()(null); /// Here we test the output of the matchConfigSpec function describe("Inputs", () => { @@ -53,7 +44,7 @@ describe("Inputs", () => { mediasources: ["filebrowser"], testListUnion: [ { - union: { [unionSelectKey]: "lnd", [unionValueKey]: { name: "string" } }, + union: { selection: "lnd", value: { name: "string" } }, }, ], rpc: { @@ -92,8 +83,8 @@ describe("Inputs", () => { }, dbcache: 5, pruning: { - unionSelectKey: "disabled", - unionValueKey: {}, + selection: "disabled", + value: {}, }, blockfilters: { blockfilterindex: false, diff --git a/sdk/lib/util/getRandomCharInSet.ts b/sdk/lib/util/getRandomCharInSet.ts index b26eef648..7914209a3 100644 --- a/sdk/lib/util/getRandomCharInSet.ts +++ b/sdk/lib/util/getRandomCharInSet.ts @@ -1,9 +1,10 @@ // a,g,h,A-Z,,,,- -import * as crypto from "crypto" export function getRandomCharInSet(charset: string): string { const set = stringToCharSet(charset) - let charIdx = crypto.randomInt(0, set.len) + let charIdx = Math.floor( + (crypto.getRandomValues(new Uint32Array(1))[0] / 2 ** 32) * set.len, + ) for (let range of set.ranges) { if (range.len > charIdx) { return String.fromCharCode(range.start.charCodeAt(0) + charIdx) diff --git a/sdk/lib/util/getServiceInterface.ts b/sdk/lib/util/getServiceInterface.ts index 737cbbc16..a2f17be10 100644 --- a/sdk/lib/util/getServiceInterface.ts +++ b/sdk/lib/util/getServiceInterface.ts @@ -80,7 +80,7 @@ export const addressHostToUrl = ( ): UrlString[] => { const res = [] const fmt = (scheme: string | null, host: HostnameInfo, port: number) => { - const includePort = + const excludePort = scheme && scheme in knownProtocols && port === knownProtocols[scheme as keyof typeof knownProtocols].defaultPort @@ -96,7 +96,7 @@ export const addressHostToUrl = ( } return `${scheme ? `${scheme}://` : ""}${ username ? `${username}@` : "" - }${hostname}${includePort ? `:${port}` : ""}${suffix}` + }${hostname}${excludePort ? "" : `:${port}`}${suffix}` } if (host.hostname.sslPort !== null) { res.push(fmt(sslScheme, host, host.hostname.sslPort)) diff --git a/sdk/lib/util/index.browser.ts b/sdk/lib/util/index.browser.ts index 6ff7ed01c..94339e7f7 100644 --- a/sdk/lib/util/index.browser.ts +++ b/sdk/lib/util/index.browser.ts @@ -1,25 +1,10 @@ import * as T from "../types" +/// Currently being used +export { addressHostToUrl } from "./getServiceInterface" +export { getDefaultString } from "./getDefaultString" + +/// Not being used, but known to be browser compatible export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" export { getServiceInterfaces } from "./getServiceInterfaces" -// prettier-ignore -export type FlattenIntersection = -T extends ArrayLike ? T : -T extends object ? {} & {[P in keyof T]: T[P]} : - T; - -export type _ = FlattenIntersection - -export const isKnownError = (e: unknown): e is T.KnownError => - e instanceof Object && ("error" in e || "error-code" in e) - -declare const affine: unique symbol - -export type Affine = { [affine]: A } - -type NeverPossible = { [affine]: string } -export type NoAny = NeverPossible extends A - ? keyof NeverPossible extends keyof A - ? never - : A - : A +export * from "./typeHelpers" diff --git a/sdk/lib/util/index.ts b/sdk/lib/util/index.ts index 9b19aeb38..63629dfaa 100644 --- a/sdk/lib/util/index.ts +++ b/sdk/lib/util/index.ts @@ -1,5 +1,3 @@ -import * as T from "../types" - import "./nullIfEmpty" import "./fileHelper" import "../store/getStore" @@ -12,26 +10,5 @@ export { GetServiceInterface, getServiceInterface } from "./getServiceInterface" export { getServiceInterfaces } from "./getServiceInterfaces" export { addressHostToUrl } from "./getServiceInterface" export { hostnameInfoToAddress } from "./Hostname" -// prettier-ignore -export type FlattenIntersection = -T extends ArrayLike ? T : -T extends object ? {} & {[P in keyof T]: T[P]} : - T; - -export type _ = FlattenIntersection - -export const isKnownError = (e: unknown): e is T.KnownError => - e instanceof Object && ("error" in e || "error-code" in e) - -declare const affine: unique symbol - -export type Affine = { [affine]: A } - -type NeverPossible = { [affine]: string } -export type NoAny = NeverPossible extends A - ? keyof NeverPossible extends keyof A - ? never - : A - : A - +export * from "./typeHelpers" export { getDefaultString } from "./getDefaultString" diff --git a/sdk/lib/util/typeHelpers.ts b/sdk/lib/util/typeHelpers.ts new file mode 100644 index 000000000..f45a46f1e --- /dev/null +++ b/sdk/lib/util/typeHelpers.ts @@ -0,0 +1,23 @@ +import * as T from "../types" + +// prettier-ignore +export type FlattenIntersection = +T extends ArrayLike ? T : +T extends object ? {} & {[P in keyof T]: T[P]} : + T; + +export type _ = FlattenIntersection + +export const isKnownError = (e: unknown): e is T.KnownError => + e instanceof Object && ("error" in e || "error-code" in e) + +declare const affine: unique symbol + +export type Affine = { [affine]: A } + +type NeverPossible = { [affine]: string } +export type NoAny = NeverPossible extends A + ? keyof NeverPossible extends keyof A + ? never + : A + : A diff --git a/sdk/scripts/oldSpecToBuilder.ts b/sdk/scripts/oldSpecToBuilder.ts index ce8ea4e5f..6dd726e1b 100644 --- a/sdk/scripts/oldSpecToBuilder.ts +++ b/sdk/scripts/oldSpecToBuilder.ts @@ -285,26 +285,26 @@ const {Config, List, Value, Variants} = sdk maxLength: null, })})` } - case "number": { - return `${rangeToTodoComment(value?.range)}List.number(${JSON.stringify( - { - name: value.name || null, - minLength: null, - maxLength: null, - default: value.default || null, - description: value.description || null, - warning: value.warning || null, - }, - null, - 2, - )}, ${JSON.stringify({ - integer: value?.spec?.integral || false, - min: null, - max: null, - units: value?.spec?.units || null, - placeholder: value?.spec?.placeholder || null, - })})` - } + // case "number": { + // return `${rangeToTodoComment(value?.range)}List.number(${JSON.stringify( + // { + // name: value.name || null, + // minLength: null, + // maxLength: null, + // default: value.default || null, + // description: value.description || null, + // warning: value.warning || null, + // }, + // null, + // 2, + // )}, ${JSON.stringify({ + // integer: value?.spec?.integral || false, + // min: null, + // max: null, + // units: value?.spec?.units || null, + // placeholder: value?.spec?.placeholder || null, + // })})` + // } case "enum": { return "/* error!! list.enum */" } diff --git a/web/package-lock.json b/web/package-lock.json index 8a48b18fa..b1f1802e0 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -20,20 +20,22 @@ "@angular/service-worker": "^14.2.2", "@ionic/angular": "^6.1.15", "@materia-ui/ngx-monaco-editor": "^6.0.0", - "@ng-web-apis/common": "^2.0.0", - "@ng-web-apis/mutation-observer": "^2.0.0", - "@ng-web-apis/resize-observer": "^2.0.0", + "@ng-web-apis/common": "^3.0.6", + "@ng-web-apis/mutation-observer": "^3.2.1", + "@ng-web-apis/resize-observer": "^3.2.1", "@noble/curves": "^1.4.0", "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", - "@taiga-ui/addon-charts": "3.20.0", - "@taiga-ui/cdk": "3.20.0", - "@taiga-ui/core": "3.20.0", - "@taiga-ui/icons": "3.20.0", - "@taiga-ui/kit": "3.20.0", + "@taiga-ui/addon-charts": "3.84.0", + "@taiga-ui/cdk": "3.84.0", + "@taiga-ui/core": "3.84.0", + "@taiga-ui/experimental": "3.84.0", + "@taiga-ui/icons": "3.84.0", + "@taiga-ui/kit": "3.84.0", "@tinkoff/ng-dompurify": "4.0.0", + "@tinkoff/ng-event-plugins": "3.2.0", "angular-svg-round-progressbar": "^9.0.0", "ansi-to-html": "^0.7.2", "base64-js": "^1.5.1", @@ -111,1873 +113,7 @@ "rxjs": ">=7.0.0" } }, - "../patch-db/client/node_modules/@babel/code-frame": { - "version": "7.21.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/highlight": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "../patch-db/client/node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "../patch-db/client/node_modules/@babel/highlight": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "../patch-db/client/node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "../patch-db/client/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "../patch-db/client/node_modules/@tsconfig/node10": { - "version": "1.0.9", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@tsconfig/node12": { - "version": "1.0.11", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@tsconfig/node14": { - "version": "1.0.3", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@tsconfig/node16": { - "version": "1.0.4", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@types/node": { - "version": "18.15.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@types/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/@types/uuid": { - "version": "8.3.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/acorn": { - "version": "8.8.2", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "../patch-db/client/node_modules/acorn-walk": { - "version": "8.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "../patch-db/client/node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/ansi-escapes": { - "version": "4.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/ansi-regex": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "../patch-db/client/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "../patch-db/client/node_modules/arg": { - "version": "4.1.3", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/argparse": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "../patch-db/client/node_modules/astral-regex": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/brace-expansion": { - "version": "1.1.11", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "../patch-db/client/node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/builtin-modules": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "../patch-db/client/node_modules/callsites": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "../patch-db/client/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "../patch-db/client/node_modules/ci-info": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "../patch-db/client/node_modules/cli-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/cli-truncate": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "../patch-db/client/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/colorette": { - "version": "2.0.20", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/commander": { - "version": "10.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "../patch-db/client/node_modules/compare-versions": { - "version": "3.6.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/cosmiconfig": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "../patch-db/client/node_modules/create-require": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "../patch-db/client/node_modules/debug": { - "version": "4.3.4", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "../patch-db/client/node_modules/diff": { - "version": "4.0.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "../patch-db/client/node_modules/eastasianwidth": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/emoji-regex": { - "version": "9.2.2", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/error-ex": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "../patch-db/client/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "../patch-db/client/node_modules/esprima": { - "version": "4.0.1", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/execa": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "../patch-db/client/node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/find-up": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/find-versions": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver-regex": "^3.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/fs.realpath": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/function-bind": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/get-stream": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "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" - } - }, - "../patch-db/client/node_modules/has": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "../patch-db/client/node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/human-signals": { - "version": "4.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" - } - }, - "../patch-db/client/node_modules/husky": { - "version": "4.3.8", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.6.0", - "cosmiconfig": "^7.0.0", - "find-versions": "^4.0.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^5.0.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - }, - "bin": { - "husky-run": "bin/run.js", - "husky-upgrade": "lib/upgrader/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/husky" - } - }, - "../patch-db/client/node_modules/import-fresh": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/inflight": { - "version": "1.0.6", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "../patch-db/client/node_modules/inherits": { - "version": "2.0.4", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/is-arrayish": { - "version": "0.2.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/is-core-module": { - "version": "2.12.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "../patch-db/client/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "../patch-db/client/node_modules/is-stream": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/js-yaml": { - "version": "3.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "../patch-db/client/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/lilconfig": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "../patch-db/client/node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/lint-staged": { - "version": "13.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "../patch-db/client/node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "../patch-db/client/node_modules/lint-staged/node_modules/yaml": { - "version": "2.3.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 14" - } - }, - "../patch-db/client/node_modules/listr2": { - "version": "5.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "../patch-db/client/node_modules/listr2/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/listr2/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/listr2/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/listr2/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/listr2/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/log-update": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/micromatch": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "../patch-db/client/node_modules/mimic-fn": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "../patch-db/client/node_modules/minimist": { - "version": "1.2.8", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "../patch-db/client/node_modules/mkdirp": { - "version": "0.5.6", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "../patch-db/client/node_modules/ms": { - "version": "2.1.2", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "../patch-db/client/node_modules/npm-run-path": { - "version": "5.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/object-inspect": { - "version": "1.12.3", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "../patch-db/client/node_modules/once": { - "version": "1.4.0", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "../patch-db/client/node_modules/onetime": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/opencollective-postinstall": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "bin": { - "opencollective-postinstall": "index.js" - } - }, - "../patch-db/client/node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/parent-module": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "../patch-db/client/node_modules/parse-json": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/path-is-absolute": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "../patch-db/client/node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/path-type": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "../patch-db/client/node_modules/pidtree": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "../patch-db/client/node_modules/pkg-dir": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "../patch-db/client/node_modules/please-upgrade-node": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver-compare": "^1.0.0" - } - }, - "../patch-db/client/node_modules/prettier": { - "version": "2.8.8", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "../patch-db/client/node_modules/resolve": { - "version": "1.22.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "../patch-db/client/node_modules/resolve-from": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/restore-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "../patch-db/client/node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/rfdc": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/rxjs": { - "version": "7.8.1", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "../patch-db/client/node_modules/semver": { - "version": "5.7.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "../patch-db/client/node_modules/semver-compare": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/semver-regex": { - "version": "3.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/slice-ansi": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "../patch-db/client/node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "../patch-db/client/node_modules/sorted-btree": { - "version": "1.5.0", - "license": "MIT" - }, - "../patch-db/client/node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "../patch-db/client/node_modules/string-argv": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "../patch-db/client/node_modules/string-width": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/strip-ansi": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "../patch-db/client/node_modules/strip-final-newline": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "../patch-db/client/node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "../patch-db/client/node_modules/ts-node": { - "version": "10.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "../patch-db/client/node_modules/tslib": { - "version": "2.5.3", - "license": "0BSD" - }, - "../patch-db/client/node_modules/tslint": { - "version": "6.1.3", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/chalk": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/tslint/node_modules/commander": { - "version": "2.20.3", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/tslint/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/tslint/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "../patch-db/client/node_modules/tsutils": { - "version": "2.29.0", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "../patch-db/client/node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "dev": true, - "license": "0BSD" - }, - "../patch-db/client/node_modules/type-fest": { - "version": "0.21.3", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "../patch-db/client/node_modules/typescript": { - "version": "4.9.5", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "../patch-db/client/node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "../patch-db/client/node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "../patch-db/client/node_modules/which-pm-runs": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "../patch-db/client/node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "../patch-db/client/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" - }, - "../patch-db/client/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "../patch-db/client/node_modules/wrappy": { - "version": "1.0.2", - "dev": true, - "license": "ISC" - }, - "../patch-db/client/node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "../patch-db/client/node_modules/yn": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "../patch-db/client/node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "../sdk/dist": { - "name": "@start9labs/start-sdk", "version": "0.3.6-alpha5", "license": "MIT", "dependencies": { @@ -2002,14 +138,16 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "dev": true, - "license": "MIT" + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true }, "node_modules/@ampproject/remapping": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.1.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -2019,11 +157,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1402.3", + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", + "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", "devOptional": true, - "license": "MIT", "dependencies": { - "@angular-devkit/core": "14.2.3", + "@angular-devkit/core": "14.2.13", "rxjs": "6.6.7" }, "engines": { @@ -2034,8 +173,9 @@ }, "node_modules/@angular-devkit/architect/node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "devOptional": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -2045,18 +185,20 @@ }, "node_modules/@angular-devkit/architect/node_modules/tslib": { "version": "1.14.1", - "devOptional": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "devOptional": true }, "node_modules/@angular-devkit/build-angular": { - "version": "14.2.3", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", + "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1402.3", - "@angular-devkit/build-webpack": "0.1402.3", - "@angular-devkit/core": "14.2.3", + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/build-webpack": "0.1402.13", + "@angular-devkit/core": "14.2.13", "@babel/core": "7.18.10", "@babel/generator": "7.18.12", "@babel/helper-annotate-as-pure": "7.18.6", @@ -2067,7 +209,7 @@ "@babel/runtime": "7.18.9", "@babel/template": "7.18.10", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "14.2.3", + "@ngtools/webpack": "14.2.13", "ansi-colors": "4.1.3", "babel-loader": "8.2.5", "babel-plugin-istanbul": "6.1.1", @@ -2085,14 +227,14 @@ "less": "4.1.3", "less-loader": "11.0.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.0", + "loader-utils": "3.2.1", "mini-css-extract-plugin": "2.6.1", "minimatch": "5.1.0", "open": "8.4.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "6.0.1", "piscina": "3.2.0", - "postcss": "8.4.16", + "postcss": "8.4.31", "postcss-import": "15.0.0", "postcss-loader": "7.0.1", "postcss-preset-env": "7.8.0", @@ -2101,7 +243,7 @@ "rxjs": "6.6.7", "sass": "1.54.4", "sass-loader": "13.0.2", - "semver": "7.3.7", + "semver": "7.5.3", "source-map-loader": "4.0.0", "source-map-support": "0.5.21", "stylus": "0.59.0", @@ -2110,7 +252,7 @@ "text-table": "0.2.0", "tree-kill": "1.2.2", "tslib": "2.4.0", - "webpack": "5.74.0", + "webpack": "5.76.1", "webpack-dev-middleware": "5.3.3", "webpack-dev-server": "4.11.0", "webpack-merge": "5.8.0", @@ -2155,25 +297,194 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": { - "version": "14.2.3", + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^14.0.0", - "typescript": ">=4.6.2 <4.9", - "webpack": "^5.54.0" + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" } }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -2183,15 +494,88 @@ }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "0BSD" + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1402.3", + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", + "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", "dev": true, - "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1402.3", + "@angular-devkit/architect": "0.1402.13", "rxjs": "6.6.7" }, "engines": { @@ -2206,8 +590,9 @@ }, "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -2217,13 +602,14 @@ }, "node_modules/@angular-devkit/build-webpack/node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/@angular-devkit/core": { - "version": "14.2.3", - "devOptional": true, - "license": "MIT", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", + "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", "dependencies": { "ajv": "8.11.0", "ajv-formats": "2.1.1", @@ -2247,8 +633,8 @@ }, "node_modules/@angular-devkit/core/node_modules/rxjs": { "version": "6.6.7", - "devOptional": true, - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dependencies": { "tslib": "^1.9.0" }, @@ -2258,15 +644,15 @@ }, "node_modules/@angular-devkit/core/node_modules/tslib": { "version": "1.14.1", - "devOptional": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@angular-devkit/schematics": { - "version": "14.2.3", - "devOptional": true, - "license": "MIT", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.13.tgz", + "integrity": "sha512-2zczyeNzeBcrT2HOysv52X9SH3tZoHfWJvVf6H0SIa74rfDKEl7hFpKNXnh3x8sIMLj5mZn05n5RCqGxCczcIg==", "dependencies": { - "@angular-devkit/core": "14.2.3", + "@angular-devkit/core": "14.2.13", "jsonc-parser": "3.1.0", "magic-string": "0.26.2", "ora": "5.4.1", @@ -2280,8 +666,8 @@ }, "node_modules/@angular-devkit/schematics/node_modules/rxjs": { "version": "6.6.7", - "devOptional": true, - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dependencies": { "tslib": "^1.9.0" }, @@ -2291,12 +677,13 @@ }, "node_modules/@angular-devkit/schematics/node_modules/tslib": { "version": "1.14.1", - "devOptional": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@angular/animations": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", + "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", "dependencies": { "tslib": "^2.3.0" }, @@ -2304,18 +691,19 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.2" + "@angular/core": "14.3.0" } }, "node_modules/@angular/cli": { - "version": "14.2.3", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.13.tgz", + "integrity": "sha512-I5EepRem2CCyS3GDzQxZ2ZrqQwVqoGoLY+ZQhsK1QGWUnUyFOjbv3OlUGxRUYwcedu19V1EBAKjmQ96HzMIcVQ==", "devOptional": true, - "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1402.3", - "@angular-devkit/core": "14.2.3", - "@angular-devkit/schematics": "14.2.3", - "@schematics/angular": "14.2.3", + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "@schematics/angular": "14.2.13", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "debug": "4.3.4", @@ -2328,7 +716,7 @@ "ora": "5.4.1", "pacote": "13.6.2", "resolve": "1.22.1", - "semver": "7.3.7", + "semver": "7.5.3", "symbol-observable": "4.0.0", "uuid": "8.3.2", "yargs": "17.5.1" @@ -2343,8 +731,9 @@ } }, "node_modules/@angular/common": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", + "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -2352,13 +741,14 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.2", + "@angular/core": "14.3.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", + "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", "dependencies": { "tslib": "^2.3.0" }, @@ -2366,7 +756,7 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/core": "14.2.2" + "@angular/core": "14.3.0" }, "peerDependenciesMeta": { "@angular/core": { @@ -2375,9 +765,10 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "14.2.2", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", + "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.17.2", "chokidar": "^3.0.0", @@ -2399,13 +790,14 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/compiler": "14.2.2", + "@angular/compiler": "14.3.0", "typescript": ">=4.6.2 <4.9" } }, "node_modules/@angular/core": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", + "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", "dependencies": { "tslib": "^2.3.0" }, @@ -2414,12 +806,13 @@ }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4" + "zone.js": "~0.11.4 || ~0.12.0" } }, "node_modules/@angular/forms": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", + "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", "dependencies": { "tslib": "^2.3.0" }, @@ -2427,23 +820,25 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.2", - "@angular/core": "14.2.2", - "@angular/platform-browser": "14.2.2", + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "14.2.2", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-14.3.0.tgz", + "integrity": "sha512-Sij3OQzj1UGs1O8H9PxVAY/o27+oqZwQRnib66rsWvtbIBTjHp4FV3dTs5iVcr62GGv4V4Mff/2I82NP10GPQg==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || >=16.10.0" } }, "node_modules/@angular/platform-browser": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", + "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", "dependencies": { "tslib": "^2.3.0" }, @@ -2451,9 +846,9 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/animations": "14.2.2", - "@angular/common": "14.2.2", - "@angular/core": "14.2.2" + "@angular/animations": "14.3.0", + "@angular/common": "14.3.0", + "@angular/core": "14.3.0" }, "peerDependenciesMeta": { "@angular/animations": { @@ -2462,8 +857,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", + "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", "dependencies": { "tslib": "^2.3.0" }, @@ -2471,18 +867,19 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.2", - "@angular/compiler": "14.2.2", - "@angular/core": "14.2.2", - "@angular/platform-browser": "14.2.2" + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0" } }, "node_modules/@angular/pwa": { - "version": "14.1.0", - "license": "MIT", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular/pwa/-/pwa-14.2.13.tgz", + "integrity": "sha512-WPkTgT3+VC/KeZMydZnTQJWTG5IVTSdkJfqmNQWfHXzpmm1CG4KvFRj3xEOXvaDmcL56nnqKhL/o66kpai15Qw==", "dependencies": { - "@angular-devkit/schematics": "14.1.0", - "@schematics/angular": "14.1.0", + "@angular-devkit/schematics": "14.2.13", + "@schematics/angular": "14.2.13", "parse5-html-rewriting-stream": "6.0.1" }, "engines": { @@ -2499,77 +896,10 @@ } } }, - "node_modules/@angular/pwa/node_modules/@angular-devkit/core": { - "version": "14.1.0", - "license": "MIT", - "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular/pwa/node_modules/@angular-devkit/schematics": { - "version": "14.1.0", - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "14.1.0", - "jsonc-parser": "3.1.0", - "magic-string": "0.26.2", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/pwa/node_modules/@schematics/angular": { - "version": "14.1.0", - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "14.1.0", - "@angular-devkit/schematics": "14.1.0", - "jsonc-parser": "3.1.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/pwa/node_modules/rxjs": { - "version": "6.6.7", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular/pwa/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, "node_modules/@angular/router": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", + "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", "dependencies": { "tslib": "^2.3.0" }, @@ -2577,15 +907,16 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.2", - "@angular/core": "14.2.2", - "@angular/platform-browser": "14.2.2", + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "14.2.2", - "license": "MIT", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-14.3.0.tgz", + "integrity": "sha512-i5O7m1gQijWm7cgva0XTmOVBFrPrttNxFDwoMLMYCh8rHOCQUQ4DcVO1qTBPWU4SrY5BYPEvR+r05dYQLFYCBw==", "dependencies": { "tslib": "^2.3.0" }, @@ -2596,38 +927,43 @@ "node": "^14.15.0 || >=16.10.0" }, "peerDependencies": { - "@angular/common": "14.2.2", - "@angular/core": "14.2.2" + "@angular/common": "14.3.0", + "@angular/core": "14.3.0" } }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -2654,17 +990,19 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.18.10", "@jridgewell/gen-mapping": "^0.3.2", @@ -2675,13 +1013,14 @@ } }, "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -2689,8 +1028,9 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.18.6" }, @@ -2699,54 +1039,58 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", + "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.19.1", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", + "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2755,13 +1099,36 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.19.0", + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/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-create-regexp-features-plugin": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", + "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -2770,10 +1137,32 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/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-define-polyfill-provider": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", "@babel/helper-plugin-utils": "^7.16.7", @@ -2787,123 +1176,102 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", + "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.19.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2912,122 +1280,214 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.19.1", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", + "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", + "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", + "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-wrap-function": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", + "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-optimise-call-expression": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", + "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.9" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", + "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/helper-function-name": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "dev": true, - "license": "MIT", "bin": { "parser": "bin/babel-parser.js" }, @@ -3036,11 +1496,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", + "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3050,13 +1511,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", + "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3067,8 +1529,10 @@ }, "node_modules/@babel/plugin-proposal-async-generator-functions": { "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-plugin-utils": "^7.18.9", @@ -3084,8 +1548,10 @@ }, "node_modules/@babel/plugin-proposal-class-properties": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -3098,12 +1564,14 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -3115,8 +1583,10 @@ }, "node_modules/@babel/plugin-proposal-dynamic-import": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -3130,8 +1600,10 @@ }, "node_modules/@babel/plugin-proposal-export-namespace-from": { "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -3145,8 +1617,10 @@ }, "node_modules/@babel/plugin-proposal-json-strings": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -3159,11 +1633,13 @@ } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -3175,8 +1651,10 @@ }, "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -3190,8 +1668,10 @@ }, "node_modules/@babel/plugin-proposal-numeric-separator": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -3204,15 +1684,17 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "dev": true, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -3223,8 +1705,10 @@ }, "node_modules/@babel/plugin-proposal-optional-catch-binding": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -3237,12 +1721,14 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -3254,8 +1740,10 @@ }, "node_modules/@babel/plugin-proposal-private-methods": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -3268,13 +1756,15 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -3286,8 +1776,10 @@ }, "node_modules/@babel/plugin-proposal-unicode-property-regex": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -3301,8 +1793,9 @@ }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3312,8 +1805,9 @@ }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -3323,8 +1817,9 @@ }, "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -3337,8 +1832,9 @@ }, "node_modules/@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3348,8 +1844,9 @@ }, "node_modules/@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -3358,11 +1855,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", + "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3373,8 +1871,9 @@ }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3384,8 +1883,9 @@ }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -3395,8 +1895,9 @@ }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3406,8 +1907,9 @@ }, "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -3417,8 +1919,9 @@ }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3428,8 +1931,9 @@ }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3439,8 +1943,9 @@ }, "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -3450,8 +1955,9 @@ }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -3464,8 +1970,9 @@ }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -3477,11 +1984,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", + "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3492,8 +2000,9 @@ }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6", @@ -3507,11 +2016,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", + "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3521,11 +2031,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", + "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3535,18 +2046,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", + "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", "globals": "^11.1.0" }, "engines": { @@ -3556,12 +2067,26 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.18.9", + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", + "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/template": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3570,12 +2095,27 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.18.13", + "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", + "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3585,12 +2125,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", + "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3600,11 +2141,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", + "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3614,12 +2156,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", + "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3629,11 +2172,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.18.8", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", + "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3643,13 +2188,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", + "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3659,11 +2205,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", + "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3673,11 +2220,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", + "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3687,13 +2235,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", + "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3703,14 +2251,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", + "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3720,15 +2268,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", + "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3738,12 +2286,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", + "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3753,12 +2302,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", + "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3768,11 +2318,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", + "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3782,12 +2333,30 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", + "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", + "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -3797,11 +2366,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", + "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3811,11 +2381,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", + "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3825,12 +2396,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", + "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.24.7", + "regenerator-transform": "^0.15.2" }, "engines": { "node": ">=6.9.0" @@ -3840,11 +2412,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", + "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3855,8 +2428,9 @@ }, "node_modules/@babel/plugin-transform-runtime": { "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.9", @@ -3873,19 +2447,21 @@ } }, "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", + "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3895,12 +2471,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", + "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3910,11 +2487,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", + "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3924,11 +2502,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", + "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3938,11 +2517,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", + "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3952,11 +2532,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", + "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3966,12 +2547,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", + "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3982,8 +2564,9 @@ }, "node_modules/@babel/preset-env": { "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/compat-data": "^7.18.8", "@babel/helper-compilation-targets": "^7.18.9", @@ -4069,17 +2652,19 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/preset-modules": { - "version": "0.1.5", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", @@ -4088,13 +2673,20 @@ "esutils": "^2.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, "node_modules/@babel/runtime": { "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", "dev": true, - "license": "MIT", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -4104,8 +2696,9 @@ }, "node_modules/@babel/template": { "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.18.10", @@ -4116,19 +2709,20 @@ } }, "node_modules/@babel/traverse": { - "version": "7.19.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.19.1", - "@babel/types": "^7.19.0", - "debug": "^4.1.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -4136,12 +2730,14 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.19.0", - "@jridgewell/gen-mapping": "^0.3.2", + "@babel/types": "^7.24.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -4149,25 +2745,27 @@ } }, "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/types": { - "version": "7.19.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -4176,8 +2774,9 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -4187,8 +2786,9 @@ }, "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4196,8 +2796,9 @@ }, "node_modules/@csstools/postcss-cascade-layers": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/selector-specificity": "^2.0.2", "postcss-selector-parser": "^6.0.10" @@ -4215,8 +2816,9 @@ }, "node_modules/@csstools/postcss-color-function": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -4234,8 +2836,9 @@ }, "node_modules/@csstools/postcss-font-format-keywords": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4252,8 +2855,9 @@ }, "node_modules/@csstools/postcss-hwb-function": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4270,8 +2874,9 @@ }, "node_modules/@csstools/postcss-ic-unit": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -4289,8 +2894,9 @@ }, "node_modules/@csstools/postcss-is-pseudo-class": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" @@ -4308,8 +2914,9 @@ }, "node_modules/@csstools/postcss-nested-calc": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4326,8 +2933,9 @@ }, "node_modules/@csstools/postcss-normalize-display-values": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4344,8 +2952,9 @@ }, "node_modules/@csstools/postcss-oklab-function": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -4363,8 +2972,9 @@ }, "node_modules/@csstools/postcss-progressive-custom-properties": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4377,8 +2987,9 @@ }, "node_modules/@csstools/postcss-stepped-value-functions": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4395,8 +3006,9 @@ }, "node_modules/@csstools/postcss-text-decoration-shorthand": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4413,8 +3025,9 @@ }, "node_modules/@csstools/postcss-trigonometric-functions": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -4431,8 +3044,9 @@ }, "node_modules/@csstools/postcss-unset-value": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", "dev": true, - "license": "CC0-1.0", "engines": { "node": "^12 || ^14 || >=16" }, @@ -4445,39 +3059,59 @@ } }, "node_modules/@csstools/selector-specificity": { - "version": "2.0.2", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "dev": true, - "license": "CC0-1.0", "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2", "postcss-selector-parser": "^6.0.10" } }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "devOptional": true }, "node_modules/@ionic/angular": { - "version": "6.2.7", - "license": "MIT", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-6.7.5.tgz", + "integrity": "sha512-nV8HP7RedjYkIAT8nVr5ifHNT0D3XzA74RPG3/WCCFJKunERNJ9SBiNkCTWhUpSkqsYYwEB4+SOOHz+R5NLk/w==", "dependencies": { - "@ionic/core": "^6.2.7", + "@ionic/core": "6.7.5", + "ionicons": "^6.1.3", "jsonc-parser": "^3.0.0", "tslib": "^2.0.0" }, @@ -4490,9 +3124,10 @@ } }, "node_modules/@ionic/cli": { - "version": "6.20.1", + "version": "6.20.9", + "resolved": "https://registry.npmjs.org/@ionic/cli/-/cli-6.20.9.tgz", + "integrity": "sha512-sItLCi7zXq1zARWIpZDinHhK8hvy+wzOx176QMOJV90BjDybkjGYu3rGu5TBjoqn104dRIZTC8rtCsnD/P3cQw==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/cli-framework": "5.1.3", "@ionic/cli-framework-output": "2.2.5", @@ -4530,8 +3165,9 @@ }, "node_modules/@ionic/cli-framework": { "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework/-/cli-framework-5.1.3.tgz", + "integrity": "sha512-T2KN/TurzNoAcc3iDt1KHU6GeEa7x9kXngMnu5xs+DzJv5HhBKjVOoo74b8rgVxdPx+dLOV8aLrorlyvsHR/tQ==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/cli-framework-output": "2.2.5", "@ionic/utils-array": "2.1.5", @@ -4555,8 +3191,9 @@ }, "node_modules/@ionic/cli-framework-output": { "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.5.tgz", + "integrity": "sha512-YeDLTnTaE6V4IDUxT8GDIep0GuRIFaR7YZDLANMuuWJZDmnTku6DP+MmQoltBeLmVvz1BAAZgk41xzxdq6H2FQ==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/utils-terminal": "2.3.3", "debug": "^4.0.0", @@ -4568,8 +3205,9 @@ }, "node_modules/@ionic/cli-framework-prompts": { "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-prompts/-/cli-framework-prompts-2.1.10.tgz", + "integrity": "sha512-h8HbA0teR0vWtGKB3ahzRbDq4yYaxfukgbOqhu9CAEJHosoFlBmDB8PbPnGFYxUg2J1MuCqeiN2ftJQYV/BO1w==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/utils-terminal": "2.3.3", "debug": "^4.0.0", @@ -4582,8 +3220,9 @@ }, "node_modules/@ionic/cli-framework-prompts/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4596,8 +3235,9 @@ }, "node_modules/@ionic/cli-framework-prompts/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, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4611,8 +3251,9 @@ }, "node_modules/@ionic/cli-framework-prompts/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4622,21 +3263,24 @@ }, "node_modules/@ionic/cli-framework-prompts/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@ionic/cli-framework-prompts/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@ionic/cli-framework-prompts/node_modules/inquirer": { "version": "7.3.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", + "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", @@ -4658,8 +3302,9 @@ }, "node_modules/@ionic/cli-framework-prompts/node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -4669,13 +3314,15 @@ }, "node_modules/@ionic/cli-framework-prompts/node_modules/rxjs/node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/@ionic/cli-framework-prompts/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, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4685,8 +3332,9 @@ }, "node_modules/@ionic/cli-framework/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4699,8 +3347,9 @@ }, "node_modules/@ionic/cli-framework/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, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4714,8 +3363,9 @@ }, "node_modules/@ionic/cli-framework/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4725,21 +3375,24 @@ }, "node_modules/@ionic/cli-framework/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@ionic/cli-framework/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@ionic/cli-framework/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, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4749,8 +3402,9 @@ }, "node_modules/@ionic/cli/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4763,8 +3417,9 @@ }, "node_modules/@ionic/cli/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, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4778,8 +3433,9 @@ }, "node_modules/@ionic/cli/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4789,21 +3445,24 @@ }, "node_modules/@ionic/cli/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/@ionic/cli/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@ionic/cli/node_modules/open": { "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, - "license": "MIT", "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" @@ -4817,8 +3476,9 @@ }, "node_modules/@ionic/cli/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, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -4827,18 +3487,20 @@ } }, "node_modules/@ionic/core": { - "version": "6.2.7", - "license": "MIT", + "version": "6.7.5", + "resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.7.5.tgz", + "integrity": "sha512-zRkRn+h/Vs3xt/EVgBdShMKDyeGOM4RU31NPF2icfu3CUTH+VrMV569MUnNjYvd1Lu2xK90pYy4TaicSWmC1Pw==", "dependencies": { - "@stencil/core": "^2.17.4", - "ionicons": "^6.0.3", + "@stencil/core": "^2.18.0", + "ionicons": "^6.1.3", "tslib": "^2.1.0" } }, "node_modules/@ionic/utils-array": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.5.tgz", + "integrity": "sha512-HD72a71IQVBmQckDwmA8RxNVMTbxnaLbgFOl+dO5tbvW9CkkSFCv41h6fUuNsSEVgngfkn0i98HDuZC8mk+lTA==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.0.0", "tslib": "^2.0.1" @@ -4849,8 +3511,9 @@ }, "node_modules/@ionic/utils-fs": { "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.6.tgz", + "integrity": "sha512-eikrNkK89CfGPmexjTfSWl4EYqsPSBh0Ka7by4F0PLc1hJZYtJxUZV3X4r5ecA8ikjicUmcbU7zJmAjmqutG/w==", "dev": true, - "license": "MIT", "dependencies": { "@types/fs-extra": "^8.0.0", "debug": "^4.0.0", @@ -4863,8 +3526,9 @@ }, "node_modules/@ionic/utils-network": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-network/-/utils-network-2.1.5.tgz", + "integrity": "sha512-HUQ1Ec4Mh2MXzzKdbbbDS6xYKwpFJ2XRY7SYXbaZT8+jiNahfHbsOfe62/p8bk41Yil7E9EagzGC2JvIFJh01w==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.0.0", "tslib": "^2.0.1" @@ -4875,8 +3539,9 @@ }, "node_modules/@ionic/utils-object": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.5.tgz", + "integrity": "sha512-XnYNSwfewUqxq+yjER1hxTKggftpNjFLJH0s37jcrNDwbzmbpFTQTVAp4ikNK4rd9DOebX/jbeZb8jfD86IYxw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.0.0", "tslib": "^2.0.1" @@ -4887,8 +3552,9 @@ }, "node_modules/@ionic/utils-process": { "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.10.tgz", + "integrity": "sha512-mZ7JEowcuGQK+SKsJXi0liYTcXd2bNMR3nE0CyTROpMECUpJeAvvaBaPGZf5ERQUPeWBVuwqAqjUmIdxhz5bxw==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/utils-object": "2.1.5", "@ionic/utils-terminal": "2.3.3", @@ -4903,8 +3569,9 @@ }, "node_modules/@ionic/utils-stream": { "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.5.tgz", + "integrity": "sha512-hkm46uHvEC05X/8PHgdJi4l4zv9VQDELZTM+Kz69odtO9zZYfnt8DkfXHJqJ+PxmtiE5mk/ehJWLnn/XAczTUw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.0.0", "tslib": "^2.0.1" @@ -4915,8 +3582,9 @@ }, "node_modules/@ionic/utils-subprocess": { "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.11.tgz", + "integrity": "sha512-6zCDixNmZCbMCy5np8klSxOZF85kuDyzZSTTQKQP90ZtYNCcPYmuFSzaqDwApJT4r5L3MY3JrqK1gLkc6xiUPw==", "dev": true, - "license": "MIT", "dependencies": { "@ionic/utils-array": "2.1.5", "@ionic/utils-fs": "3.1.6", @@ -4933,8 +3601,9 @@ }, "node_modules/@ionic/utils-terminal": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.3.tgz", + "integrity": "sha512-RnuSfNZ5fLEyX3R5mtcMY97cGD1A0NVBbarsSQ6yMMfRJ5YHU7hHVyUfvZeClbqkBC/pAqI/rYJuXKCT9YeMCQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/slice-ansi": "^4.0.0", "debug": "^4.0.0", @@ -4952,8 +3621,9 @@ }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -4967,16 +3637,18 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -4985,18 +3657,26 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.0", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -5006,65 +3686,106 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true, - "license": "MIT" + "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.15", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "dev": true, - "license": "MIT" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true + }, + "node_modules/@maskito/angular": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-1.9.0.tgz", + "integrity": "sha512-Wa/9nM9Nv0oieVZ6yxQNXfDRA4obFDR15xO16o1GKF8i9W1IdQQn+tuMRjkmx6HhJDN9+x3k8OTJ1f80BIrhjA==", + "dependencies": { + "tslib": "2.6.2" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0", + "@angular/core": ">=12.0.0", + "@angular/forms": ">=12.0.0", + "@maskito/core": "^1.9.0", + "rxjs": ">=6.0.0" + } + }, + "node_modules/@maskito/angular/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@maskito/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@maskito/core/-/core-1.9.0.tgz", + "integrity": "sha512-WQIUrwkdIUg6PzAb4Apa0RjTPHB0EqZLc9/7kWCKVIixhkITRFXFg2BhiDVSRv0mIKVlAEJcOvvjHK5T3UaIig==" + }, + "node_modules/@maskito/kit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-1.9.0.tgz", + "integrity": "sha512-LNNgOJ0tAfrPoPehvoP+ZyYF9giOYL02sOMKyDC3IcqDNA8BAU0PARmS7TNsVEBpvSuJhU6xVt40nxJaONgUdw==", + "peerDependencies": { + "@maskito/core": "^1.9.0" + } }, "node_modules/@materia-ui/ngx-monaco-editor": { "version": "6.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@materia-ui/ngx-monaco-editor/-/ngx-monaco-editor-6.0.0.tgz", + "integrity": "sha512-gTqNQjOGznZxOC0NlmKdKSGCJuTts8YmK4dsTQAGc5IgIV7cZdQWiW6AL742h0ruED6q0cAunEYjXT6jzHBoIQ==", "dependencies": { "tslib": "^2.0.0" }, @@ -5074,8 +3795,9 @@ } }, "node_modules/@ng-web-apis/common": { - "version": "2.1.0", - "license": "MIT", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@ng-web-apis/common/-/common-3.0.6.tgz", + "integrity": "sha512-ral+lzGpFS3aOCFB5DcHOI4lZhhp8GH4BnjSbngH2Xk8J0FKYdxRzvcPQVy7hS+TPUu0tW9uFVp6cC7odu3iyQ==", "dependencies": { "tslib": "^2.2.0" }, @@ -5086,8 +3808,9 @@ } }, "node_modules/@ng-web-apis/intersection-observer": { - "version": "3.0.0", - "license": "MIT", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ng-web-apis/intersection-observer/-/intersection-observer-3.2.0.tgz", + "integrity": "sha512-EhwqEZJFKR9pz55TWp82qyWTXdg8TZeMP6bUw26bVHz8CTkgrpzaXdtxurMTvJ/+gwuFy4JSJLjBeV9nfZ/SXA==", "dependencies": { "tslib": "^2.2.0" }, @@ -5097,8 +3820,9 @@ } }, "node_modules/@ng-web-apis/mutation-observer": { - "version": "2.0.0", - "license": "MIT", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@ng-web-apis/mutation-observer/-/mutation-observer-3.2.1.tgz", + "integrity": "sha512-a7krkMx0e9cfnutClwDylWjbTQVRHUP3oUik/nBvUdKlk/Q4anNww9aIKJ64VgiXR+1ZF8OmHGl0+XUzN6xP9Q==", "dependencies": { "tslib": "^2.2.0" }, @@ -5108,8 +3832,9 @@ } }, "node_modules/@ng-web-apis/resize-observer": { - "version": "2.0.0", - "license": "MIT", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@ng-web-apis/resize-observer/-/resize-observer-3.2.1.tgz", + "integrity": "sha512-r1YaZUo6DIDeR+4/C/pM4Ar0eTQBxjK0FUhYYJ512EnN8RqAn8d3a0wM8ZYunucwDICwW1sx4IIV6PZ2G77xsg==", "dependencies": { "tslib": "^2.2.0" }, @@ -5118,10 +3843,26 @@ "@ng-web-apis/common": ">=2.0.0" } }, + "node_modules/@ngtools/webpack": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", + "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, "node_modules/@noble/curves": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", - "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "dependencies": { "@noble/hashes": "1.4.0" }, @@ -5142,8 +3883,9 @@ }, "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==", "devOptional": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -5154,16 +3896,18 @@ }, "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==", "devOptional": true, - "license": "MIT", "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==", "devOptional": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -5174,8 +3918,9 @@ }, "node_modules/@npmcli/fs": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "devOptional": true, - "license": "ISC", "dependencies": { "@gar/promisify": "^1.1.3", "semver": "^7.3.5" @@ -5186,8 +3931,9 @@ }, "node_modules/@npmcli/git": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", "devOptional": true, - "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^3.0.0", "lru-cache": "^7.4.4", @@ -5203,10 +3949,20 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@npmcli/installed-package-contents": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", "devOptional": true, - "license": "ISC", "dependencies": { "npm-bundled": "^1.1.1", "npm-normalize-package-bin": "^1.0.1" @@ -5220,8 +3976,10 @@ }, "node_modules/@npmcli/move-file": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", "devOptional": true, - "license": "MIT", "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -5232,16 +3990,18 @@ }, "node_modules/@npmcli/node-gyp": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", "devOptional": true, - "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/@npmcli/promise-spawn": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", "devOptional": true, - "license": "ISC", "dependencies": { "infer-owner": "^1.0.4" }, @@ -5251,8 +4011,9 @@ }, "node_modules/@npmcli/run-script": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", "devOptional": true, - "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^2.0.0", "@npmcli/promise-spawn": "^3.0.0", @@ -5265,14 +4026,16 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.21", - "dev": true, - "license": "MIT" + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dev": true }, "node_modules/@rollup/plugin-json": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.0.8" }, @@ -5282,8 +4045,9 @@ }, "node_modules/@rollup/plugin-node-resolve": { "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", @@ -5301,8 +4065,9 @@ }, "node_modules/@rollup/pluginutils": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", @@ -5317,16 +4082,17 @@ }, "node_modules/@rollup/pluginutils/node_modules/@types/estree": { "version": "0.0.39", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true }, "node_modules/@schematics/angular": { - "version": "14.2.3", - "devOptional": true, - "license": "MIT", + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.13.tgz", + "integrity": "sha512-MLxTpTU3E8QACQ/5c0sENMR2gRiMXpGaKeD5IHY+3wyU2fUSJVB0QPU/l1WhoyZbX8N9ospBgf5UEG7taVF9rg==", "dependencies": { - "@angular-devkit/core": "14.2.3", - "@angular-devkit/schematics": "14.2.3", + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", "jsonc-parser": "3.1.0" }, "engines": { @@ -5342,15 +4108,17 @@ }, "node_modules/@start9labs/emver": { "version": "0.1.5", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@start9labs/emver/-/emver-0.1.5.tgz", + "integrity": "sha512-1dhiG03VkfEwSLx/JPKVms6srAbYFQgwfSGhwpUKMDliMXuAHGVaueStmqzVxn3JpH/HEVz0QW8w/PXHqjdiIg==" }, "node_modules/@start9labs/start-sdk": { "resolved": "../sdk/dist", "link": true }, "node_modules/@stencil/core": { - "version": "2.18.0", - "license": "MIT", + "version": "2.22.3", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz", + "integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==", "bin": { "stencil": "bin/stencil" }, @@ -5360,33 +4128,59 @@ } }, "node_modules/@taiga-ui/addon-charts": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.84.0.tgz", + "integrity": "sha512-XR7UFywnrv4NRLHOCbba63gXDYYDL4Rt0MbjnF54p5U2EXnbt2of7VbjlB6cPx40XkQqfqa3CNayYxWZP82Ijg==", "dependencies": { - "tslib": ">=2.0.0" + "tslib": "^2.6.2" }, "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", - "@ng-web-apis/common": ">=2.0.0", - "@taiga-ui/cdk": ">=3.20.0", - "@taiga-ui/core": ">=3.20.0", - "@tinkoff/ng-polymorpheus": ">=4.0.0" + "@ng-web-apis/common": "^3.0.6", + "@taiga-ui/cdk": "^3.84.0", + "@taiga-ui/core": "^3.84.0", + "@tinkoff/ng-polymorpheus": "^4.3.0" + } + }, + "node_modules/@taiga-ui/addon-commerce": { + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/addon-commerce/-/addon-commerce-3.84.0.tgz", + "integrity": "sha512-1zqLwnZLAYYcHvjH89d7JmtV2+QeZ2YnSJ3YWEMNLjGPzpev4RvQXtDfglIyu0LCyTxqpXmuzes9v/cgq2P5TQ==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0", + "@angular/core": ">=12.0.0", + "@angular/forms": ">=12.0.0", + "@maskito/angular": "^1.9.0", + "@maskito/core": "^1.9.0", + "@maskito/kit": "^1.9.0", + "@ng-web-apis/common": "^3.0.6", + "@taiga-ui/cdk": "^3.84.0", + "@taiga-ui/core": "^3.84.0", + "@taiga-ui/i18n": "^3.84.0", + "@taiga-ui/kit": "^3.84.0", + "@tinkoff/ng-polymorpheus": "^4.3.0", + "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/cdk": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.84.0.tgz", + "integrity": "sha512-0umw/CUmYNEYOCUNQVTQS53zXzxZsH/6+lj1mFVzocvfJFJWAUT6ltCH9QvxYmxSDDGWwNGg16AaVo2K+aGL0w==", "dependencies": { - "@ng-web-apis/common": "2.1.0", - "@ng-web-apis/mutation-observer": "2.0.0", - "@ng-web-apis/resize-observer": "2.0.0", - "@tinkoff/ng-event-plugins": "3.1.0", - "@tinkoff/ng-polymorpheus": "4.0.10", - "tslib": "2.5.0" + "@ng-web-apis/common": "3.0.6", + "@ng-web-apis/mutation-observer": "3.1.0", + "@ng-web-apis/resize-observer": "3.0.6", + "@tinkoff/ng-event-plugins": "3.2.0", + "@tinkoff/ng-polymorpheus": "4.3.0", + "tslib": "2.6.2" }, "optionalDependencies": { - "ng-morph": "2.1.0", + "ng-morph": "4.0.5", "parse5": "6.0.1" }, "peerDependencies": { @@ -5397,16 +4191,42 @@ "rxjs": ">=6.0.0" } }, + "node_modules/@taiga-ui/cdk/node_modules/@ng-web-apis/mutation-observer": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@ng-web-apis/mutation-observer/-/mutation-observer-3.1.0.tgz", + "integrity": "sha512-MFN0TLLBMFJJPpXkGFe9ChRCSOKvMHZRRtBq5jHWS7tv5/CtdUkqW5CU7RC9KTzZjGeMzYe0cXO4JRkjL5aZ9g==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=12.0.0", + "@ng-web-apis/common": ">=2.0.0" + } + }, + "node_modules/@taiga-ui/cdk/node_modules/@ng-web-apis/resize-observer": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@ng-web-apis/resize-observer/-/resize-observer-3.0.6.tgz", + "integrity": "sha512-QdGYdEdC0AzFonLfNOnyYyeCwnvK9jlskoeefvJN3Yyvds3ivBrrTjpeDOdiLsQpCPBp9/673imgq7355vkQow==", + "dependencies": { + "tslib": "^2.2.0" + }, + "peerDependencies": { + "@angular/core": ">=12.0.0", + "@ng-web-apis/common": ">=2.0.0" + } + }, "node_modules/@taiga-ui/cdk/node_modules/tslib": { - "version": "2.5.0", - "license": "0BSD" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/@taiga-ui/core": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.84.0.tgz", + "integrity": "sha512-FZy77z0E4qjYcszVcp+qPFkPwJPl8qXZb7t2P+juUtJvSmSn2foQHHdyhbIYN808H26tqCdgkTMG1BWQxVuDSg==", "dependencies": { - "@taiga-ui/i18n": "^3.20.0", - "tslib": ">=2.0.0" + "@taiga-ui/i18n": "^3.84.0", + "tslib": "^2.6.2" }, "peerDependencies": { "@angular/animations": ">=12.0.0", @@ -5415,52 +4235,81 @@ "@angular/forms": ">=12.0.0", "@angular/platform-browser": ">=12.0.0", "@angular/router": ">=12.0.0", - "@ng-web-apis/common": ">=2.0.0", - "@ng-web-apis/mutation-observer": ">=2.0.0", - "@taiga-ui/cdk": ">=3.20.0", - "@taiga-ui/i18n": ">=3.20.0", - "@tinkoff/ng-event-plugins": ">=3.1.0", - "@tinkoff/ng-polymorpheus": ">=4.0.0", + "@ng-web-apis/common": "^3.0.6", + "@ng-web-apis/mutation-observer": "^3.1.0", + "@taiga-ui/cdk": "^3.84.0", + "@taiga-ui/i18n": "^3.84.0", + "@tinkoff/ng-event-plugins": "^3.2.0", + "@tinkoff/ng-polymorpheus": "^4.3.0", + "rxjs": ">=6.0.0" + } + }, + "node_modules/@taiga-ui/experimental": { + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/experimental/-/experimental-3.84.0.tgz", + "integrity": "sha512-q0hNVy+EmywCG8hpZlg/+haKIFhnmxicQiSeV/D1P7CHO10safjGo0ptT6e1hYMFa5/cJZOM4OwDPen2xs17Wg==", + "dependencies": { + "tslib": "^2.6.2" + }, + "peerDependencies": { + "@angular/common": ">=12.0.0", + "@angular/core": ">=12.0.0", + "@taiga-ui/addon-commerce": "^3.84.0", + "@taiga-ui/cdk": "^3.84.0", + "@taiga-ui/core": "^3.84.0", + "@taiga-ui/kit": "^3.84.0", + "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/i18n": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.85.0.tgz", + "integrity": "sha512-CGoxfq9WY+psX5ZOfWmuQZ6OA/0CAPYJTlbHkw5sRKAyhEQ3NM/Wbx3xcwrcYRRJDnt9yOlfibz+3a+WDF2bFA==", "dependencies": { - "tslib": ">=2.0.0" + "tslib": "^2.6.2" }, "peerDependencies": { "@angular/core": ">=12.0.0", + "@ng-web-apis/common": "^3.0.6", "rxjs": ">=6.0.0" } }, "node_modules/@taiga-ui/icons": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.84.0.tgz", + "integrity": "sha512-KiH7BJRZ6wbkOHlJAS0XHq2gYnQTpRgdEogKW+GoD0da/4trCdM66vhDk2j0DwDFdBGq5U0inHJCjnskBI1nSQ==", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" + }, + "peerDependencies": { + "@taiga-ui/cdk": "^3.84.0" } }, "node_modules/@taiga-ui/kit": { - "version": "3.20.0", - "license": "Apache-2.0", + "version": "3.84.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.84.0.tgz", + "integrity": "sha512-lSUPDco5FeBYK3ESnXeEPLCdMCmNXwcdHNK/we+0ZoH4VPx/OGg2hpEP0Fej7jfGHwXFTzDbufQD0hT6WlfTAw==", "dependencies": { - "@ng-web-apis/intersection-observer": "3.0.0", + "@maskito/angular": "1.9.0", + "@maskito/core": "1.9.0", + "@maskito/kit": "1.9.0", + "@ng-web-apis/intersection-observer": "3.2.0", "text-mask-core": "5.1.2", - "tslib": ">=2.0.0" + "tslib": "^2.6.2" }, "peerDependencies": { "@angular/common": ">=12.0.0", "@angular/core": ">=12.0.0", "@angular/forms": ">=12.0.0", "@angular/router": ">=12.0.0", - "@ng-web-apis/common": ">=2.0.0", - "@ng-web-apis/mutation-observer": ">=2.0.0", - "@taiga-ui/cdk": ">=3.20.0", - "@taiga-ui/core": ">=3.20.0", - "@taiga-ui/i18n": ">=3.20.0", - "@tinkoff/ng-polymorpheus": ">=4.0.0", + "@ng-web-apis/common": "3.0.6", + "@ng-web-apis/mutation-observer": "^3.1.0", + "@ng-web-apis/resize-observer": "^3.0.6", + "@taiga-ui/cdk": "^3.84.0", + "@taiga-ui/core": "^3.84.0", + "@taiga-ui/i18n": "^3.84.0", + "@tinkoff/ng-polymorpheus": "^4.3.0", "rxjs": ">=6.0.0" } }, @@ -5479,8 +4328,9 @@ } }, "node_modules/@tinkoff/ng-event-plugins": { - "version": "3.1.0", - "license": "Apache-2.0", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@tinkoff/ng-event-plugins/-/ng-event-plugins-3.2.0.tgz", + "integrity": "sha512-n56R5xNfiytabh2WmWdQXfNU6m7dfOo3LLxlARE+DX7f5yciW2xBdDkuEHX74q8dlCuAVlW9aslSfz8c//ymwA==", "dependencies": { "tslib": "^2.2.0" }, @@ -5491,128 +4341,158 @@ } }, "node_modules/@tinkoff/ng-polymorpheus": { - "version": "4.0.10", - "license": "Apache-2.0", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tinkoff/ng-polymorpheus/-/ng-polymorpheus-4.3.0.tgz", + "integrity": "sha512-Ck/XCLuBwlUgvK22PxTlLTZhGG6I32kLqLYtDQh8N/QZZhs40+hb/78/ElFGzD567CCvrzNnueFkaOoXhuEVrw==", "dependencies": { - "tslib": "^2.0.0" + "tslib": "2.6.2" }, "peerDependencies": { - "@angular/core": ">=12.0.0" + "@angular/core": ">=12.0.0", + "@angular/platform-browser": ">=12.0.0" } }, + "node_modules/@tinkoff/ng-polymorpheus/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "devOptional": true, - "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/@ts-morph/common": { - "version": "0.9.2", - "license": "MIT", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.22.0.tgz", + "integrity": "sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==", "optional": true, "dependencies": { - "fast-glob": "^3.2.5", - "minimatch": "^3.0.4", - "mkdirp": "^1.0.4", + "fast-glob": "^3.3.2", + "minimatch": "^9.0.3", + "mkdirp": "^3.0.1", "path-browserify": "^1.0.1" } }, - "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "3.1.2", - "license": "ISC", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "optional": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "optional": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "dev": true, - "license": "MIT" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "dev": true, - "license": "MIT" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true }, "node_modules/@types/body-parser": { - "version": "1.19.2", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, - "license": "MIT", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/bonjour": { - "version": "3.5.10", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect": { - "version": "3.4.35", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, - "license": "MIT", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, "node_modules/@types/dompurify": { - "version": "2.3.4", - "license": "MIT", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", "dependencies": { "@types/trusted-types": "*" } }, "node_modules/@types/eslint": { - "version": "8.4.6", + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, - "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -5620,332 +4500,422 @@ }, "node_modules/@types/estree": { "version": "0.0.51", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true }, "node_modules/@types/express": { - "version": "4.17.14", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.31", + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/fs-extra": { - "version": "8.1.2", + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, "node_modules/@types/http-proxy": { - "version": "1.17.9", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "dev": true, - "license": "MIT" + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "dev": true, - "license": "MIT" + "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/marked": { - "version": "4.0.7", - "dev": true, - "license": "MIT" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.3.2.tgz", + "integrity": "sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==", + "dev": true }, "node_modules/@types/mime": { - "version": "3.0.1", - "dev": true, - "license": "MIT" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true }, "node_modules/@types/minimatch": { "version": "3.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "optional": true }, "node_modules/@types/mustache": { - "version": "4.2.1", - "dev": true, - "license": "MIT" + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.5.tgz", + "integrity": "sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA==", + "dev": true }, "node_modules/@types/node": { - "version": "16.11.59", + "version": "16.18.101", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.101.tgz", + "integrity": "sha512-AAsx9Rgz2IzG8KJ6tXd6ndNkVcu+GYB6U/SnFAaokSPNx2N7dcIIfnighYUNumvj6YS2q39Dejz5tT0NCV7CWA==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, - "license": "MIT" + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node-jose": { - "version": "1.1.10", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@types/node-jose/-/node-jose-1.1.13.tgz", + "integrity": "sha512-QjMd4yhwy1EvSToQn0YI3cD29YhyfxFwj7NecuymjLys2/P0FwxWnkgBlFxCai6Y3aBCe7rbwmqwJJawxlgcXw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true }, "node_modules/@types/pbkdf2": { - "version": "3.1.0", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/qs": { - "version": "6.9.7", - "dev": true, - "license": "MIT" + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", + "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "dev": true, - "license": "MIT" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true }, "node_modules/@types/resolve": { "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/retry": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, - "license": "MIT" + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } }, "node_modules/@types/serve-index": { - "version": "1.9.1", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.0", + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, - "license": "MIT", "dependencies": { - "@types/mime": "*", - "@types/node": "*" + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" } }, "node_modules/@types/slice-ansi": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true }, "node_modules/@types/sockjs": { - "version": "0.3.33", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "license": "MIT" + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, "node_modules/@types/uuid": { "version": "8.3.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==", + "dev": true }, "node_modules/@types/ws": { - "version": "8.5.3", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, - "license": "Apache-2.0", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true, - "license": "MIT" + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true }, "node_modules/@xtuc/long": { "version": "4.2.2", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", - "devOptional": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "devOptional": true }, "node_modules/abab": { "version": "2.0.6", - "dev": true, - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true }, "node_modules/abbrev": { "version": "1.1.1", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "devOptional": true }, "node_modules/accepts": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, - "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -5955,9 +4925,10 @@ } }, "node_modules/acorn": { - "version": "8.8.0", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -5966,25 +4937,41 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "dev": true, - "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peer": true, "peerDependencies": { "acorn": "^8" } }, "node_modules/acorn-walk": { - "version": "8.2.0", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, - "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } }, "node_modules/adjust-sourcemap-loader": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, - "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -5995,8 +4982,9 @@ }, "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -6008,8 +4996,9 @@ }, "node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "devOptional": true, - "license": "MIT", "dependencies": { "debug": "4" }, @@ -6018,12 +5007,11 @@ } }, "node_modules/agentkeepalive": { - "version": "4.2.1", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "devOptional": true, - "license": "MIT", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", "humanize-ms": "^1.2.1" }, "engines": { @@ -6032,8 +5020,9 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "devOptional": true, - "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -6044,7 +5033,8 @@ }, "node_modules/ajv": { "version": "8.11.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -6058,7 +5048,8 @@ }, "node_modules/ajv-formats": { "version": "2.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { "ajv": "^8.0.0" }, @@ -6073,8 +5064,9 @@ }, "node_modules/ajv-keywords": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -6084,7 +5076,8 @@ }, "node_modules/angular-svg-round-progressbar": { "version": "9.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/angular-svg-round-progressbar/-/angular-svg-round-progressbar-9.0.0.tgz", + "integrity": "sha512-q8d2AEG9u+GMAMrZY40NgejN5fHwR4iK+rRxtJ7NnMEvvuAMqt9UEtKe0SqVQHvZYE6W16L5J9yaO+TEtfRjpw==", "dependencies": { "tslib": "^2.3.0" }, @@ -6095,16 +5088,18 @@ }, "node_modules/ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "devOptional": true, - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -6117,26 +5112,29 @@ }, "node_modules/ansi-html-community": { "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true, "engines": [ "node >= 0.8.0" ], - "license": "Apache-2.0", "bin": { "ansi-html": "bin/ansi-html" } }, "node_modules/ansi-regex": { "version": "5.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "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, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -6146,7 +5144,8 @@ }, "node_modules/ansi-to-html": { "version": "0.7.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", "dependencies": { "entities": "^2.2.0" }, @@ -6158,9 +5157,10 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "devOptional": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -6171,13 +5171,16 @@ }, "node_modules/aproba": { "version": "2.0.0", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "devOptional": true }, "node_modules/are-we-there-yet": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", "devOptional": true, - "license": "ISC", "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -6188,29 +5191,34 @@ }, "node_modules/arg": { "version": "4.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/argparse": { "version": "2.0.1", - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-differ": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", "optional": true, "engines": { "node": ">=8" } }, "node_modules/array-flatten": { - "version": "2.1.2", - "dev": true, - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true }, "node_modules/array-union": { "version": "2.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "optional": true, "engines": { "node": ">=8" @@ -6218,7 +5226,8 @@ }, "node_modules/arrify": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "optional": true, "engines": { "node": ">=8" @@ -6226,8 +5235,9 @@ }, "node_modules/ast-types": { "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.1" }, @@ -6237,29 +5247,33 @@ }, "node_modules/astral-regex": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/asynckit": { "version": "0.4.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/at-least-node": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, - "license": "ISC", "engines": { "node": ">= 4.0.0" } }, "node_modules/atob": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true, - "license": "(MIT OR Apache-2.0)", "bin": { "atob": "bin/atob.js" }, @@ -6268,7 +5282,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.11", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "dev": true, "funding": [ { @@ -6278,13 +5294,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "browserslist": "^4.21.3", - "caniuse-lite": "^1.0.30001399", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.0", + "caniuse-lite": "^1.0.30001599", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -6301,8 +5320,9 @@ }, "node_modules/babel-loader": { "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, - "license": "MIT", "dependencies": { "find-cache-dir": "^3.3.1", "loader-utils": "^2.0.0", @@ -6319,8 +5339,9 @@ }, "node_modules/babel-loader/node_modules/loader-utils": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -6330,18 +5351,11 @@ "node": ">=8.9.0" } }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "object.assign": "^4.1.0" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -6355,8 +5369,9 @@ }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/compat-data": "^7.17.7", "@babel/helper-define-polyfill-provider": "^0.3.3", @@ -6367,17 +5382,19 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/babel-plugin-polyfill-corejs3": { "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.3.2", "core-js-compat": "^3.21.0" @@ -6388,8 +5405,9 @@ }, "node_modules/babel-plugin-polyfill-regenerator": { "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.3.3" }, @@ -6399,11 +5417,14 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "devOptional": 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", @@ -6417,40 +5438,47 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/base64url": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", "engines": { "node": ">=6.0.0" } }, "node_modules/batch": { "version": "0.6.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true }, "node_modules/big.js": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } }, "node_modules/binary-extensions": { - "version": "2.2.0", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { "version": "4.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -6459,6 +5487,8 @@ }, "node_modules/bl/node_modules/buffer": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { "type": "github", @@ -6473,27 +5503,27 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "node_modules/body-parser": { - "version": "1.20.0", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.10.3", - "raw-body": "2.5.1", + "qs": "6.11.0", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -6504,29 +5534,24 @@ }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/depd": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/body-parser/node_modules/qs": { - "version": "6.10.3", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -6538,42 +5563,46 @@ } }, "node_modules/bonjour-service": { - "version": "1.0.14", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, - "license": "MIT", "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } }, "node_modules/boolbase": { "version": "1.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true }, "node_modules/brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "devOptional": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "devOptional": true, - "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.21.4", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, "funding": [ { @@ -6583,14 +5612,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -6601,6 +5633,8 @@ }, "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", @@ -6615,7 +5649,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -6623,13 +5656,15 @@ }, "node_modules/buffer-from": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/builtin-modules": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -6638,25 +5673,28 @@ } }, "node_modules/builtins": { - "version": "5.0.1", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "devOptional": true, - "license": "MIT", "dependencies": { "semver": "^7.0.0" } }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", "devOptional": true, - "license": "ISC", "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", @@ -6681,13 +5719,29 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/call-bind": { - "version": "1.0.2", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, - "license": "MIT", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6695,21 +5749,25 @@ }, "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, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "5.3.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001407", + "version": "1.0.30001640", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz", + "integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==", "dev": true, "funding": [ { @@ -6719,26 +5777,32 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/cbor": { "name": "@jprochazk/cbor", "version": "0.4.9", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@jprochazk/cbor/-/cbor-0.4.9.tgz", + "integrity": "sha512-FWNnkOtWrFOLXKG2nzOHR/EnCCGZZPvatAvWXDmkTDxgjj9JHDK3DkMUHcFCY3a9weylMCSO/nLOUM170NAO0Q==" }, "node_modules/cbor-web": { "version": "8.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cbor-web/-/cbor-web-8.1.0.tgz", + "integrity": "sha512-2hWHHMVrfffgoEmsAUh8vCxHoLa1vgodtC73+C5cSarkJlwTapnqAzcHINlP6Ej0DXuP4OmmJ9LF+JaNM5Lj/g==", "engines": { "node": ">=12.19" } }, "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, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -6750,19 +5814,15 @@ }, "node_modules/chardet": { "version": "0.7.0", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "devOptional": true }, "node_modules/chokidar": { - "version": "3.5.3", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -6775,34 +5835,41 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/chownr": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "devOptional": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/chrome-trace-event": { - "version": "1.0.3", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true }, "node_modules/cipher-base": { "version": "1.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6810,15 +5877,17 @@ }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-cursor": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -6827,8 +5896,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "license": "MIT", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "engines": { "node": ">=6" }, @@ -6838,8 +5908,9 @@ }, "node_modules/cli-truncate": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" @@ -6853,8 +5924,9 @@ }, "node_modules/cli-truncate/node_modules/ansi-regex": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -6863,9 +5935,10 @@ } }, "node_modules/cli-truncate/node_modules/ansi-styles": { - "version": "6.1.1", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -6875,13 +5948,15 @@ }, "node_modules/cli-truncate/node_modules/emoji-regex": { "version": "9.2.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/cli-truncate/node_modules/is-fullwidth-code-point": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -6891,8 +5966,9 @@ }, "node_modules/cli-truncate/node_modules/slice-ansi": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" @@ -6906,8 +5982,9 @@ }, "node_modules/cli-truncate/node_modules/string-width": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -6921,9 +5998,10 @@ } }, "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.1", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -6936,16 +6014,18 @@ }, "node_modules/cli-width": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "devOptional": true, - "license": "ISC", "engines": { "node": ">= 10" } }, "node_modules/cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "devOptional": true, - "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -6954,15 +6034,17 @@ }, "node_modules/clone": { "version": "1.0.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "engines": { "node": ">=0.8" } }, "node_modules/clone-deep": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -6973,40 +6055,46 @@ } }, "node_modules/code-block-writer": { - "version": "10.1.1", - "license": "MIT", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", "optional": true }, "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, - "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/color-support": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "devOptional": true, - "license": "ISC", "bin": { "color-support": "bin.js" } }, "node_modules/colorette": { - "version": "2.0.19", - "dev": true, - "license": "MIT" + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, - "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7015,32 +6103,40 @@ } }, "node_modules/commander": { - "version": "9.4.0", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.20.0 || >=14" } }, "node_modules/commondir": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true }, "node_modules/compare-versions": { "version": "3.6.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true }, "node_modules/component-emitter": { - "version": "1.3.0", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", "dev": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/compressible": { "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, - "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -7050,8 +6146,9 @@ }, "node_modules/compression": { "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.5", "bytes": "3.0.0", @@ -7067,47 +6164,60 @@ }, "node_modules/compression/node_modules/bytes": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/compression/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "node_modules/concat-map": { "version": "0.0.1", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8" } }, "node_modules/console-control-strings": { "version": "1.1.0", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "devOptional": true }, "node_modules/content-disposition": { "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -7115,63 +6225,47 @@ "node": ">= 0.6" } }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "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" - } - ], - "license": "MIT" - }, "node_modules/content-type": { - "version": "1.0.4", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { - "version": "1.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true }, "node_modules/cookie": { - "version": "0.5.0", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true }, "node_modules/cookiejar": { "version": "2.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true }, "node_modules/copy-anything": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, - "license": "MIT", "dependencies": { "is-what": "^3.14.1" }, @@ -7181,8 +6275,9 @@ }, "node_modules/copy-webpack-plugin": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "^3.2.11", "glob-parent": "^6.0.1", @@ -7204,8 +6299,9 @@ }, "node_modules/copy-webpack-plugin/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, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -7214,14 +6310,15 @@ } }, "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -7232,20 +6329,22 @@ } }, "node_modules/core-js": { - "version": "3.25.2", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", + "integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==", "hasInstallScript": true, - "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, "node_modules/core-js-compat": { - "version": "3.25.2", + "version": "3.37.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", + "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", "dev": true, - "license": "MIT", "dependencies": { - "browserslist": "^4.21.4" + "browserslist": "^4.23.0" }, "funding": { "type": "opencollective", @@ -7254,13 +6353,15 @@ }, "node_modules/core-util-is": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cosmiconfig": { - "version": "7.0.1", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, - "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -7274,7 +6375,8 @@ }, "node_modules/create-hash": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -7285,7 +6387,8 @@ }, "node_modules/create-hmac": { "version": "1.1.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -7297,13 +6400,15 @@ }, "node_modules/create-require": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "node_modules/critters": { "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", "dev": true, - "license": "Apache-2.0", "dependencies": { "chalk": "^4.1.0", "css-select": "^4.2.0", @@ -7315,8 +6420,9 @@ }, "node_modules/critters/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7329,8 +6435,9 @@ }, "node_modules/critters/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, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7344,8 +6451,9 @@ }, "node_modules/critters/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7355,21 +6463,24 @@ }, "node_modules/critters/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/critters/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/critters/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, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7379,8 +6490,9 @@ }, "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, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -7392,8 +6504,9 @@ }, "node_modules/css-blank-pseudo": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -7409,8 +6522,9 @@ }, "node_modules/css-has-pseudo": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -7426,8 +6540,9 @@ }, "node_modules/css-loader": { "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", "dev": true, - "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.7", @@ -7451,8 +6566,9 @@ }, "node_modules/css-prefers-color-scheme": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", "dev": true, - "license": "CC0-1.0", "bin": { "css-prefers-color-scheme": "dist/cli.cjs" }, @@ -7465,8 +6581,9 @@ }, "node_modules/css-select": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -7480,8 +6597,9 @@ }, "node_modules/css-what": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -7490,18 +6608,26 @@ } }, "node_modules/cssdb": { - "version": "7.0.1", + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.11.2.tgz", + "integrity": "sha512-lhQ32TFkc1X4eTefGfYPvgovRSzIMofHkigfH8nWtyRL4XJLsRhJFreRvEgKzept7x1rjBuy3J/MurXLaFxW/A==", "dev": true, - "license": "CC0-1.0", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] }, "node_modules/cssesc": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, - "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -7511,21 +6637,30 @@ }, "node_modules/cuint": { "version": "0.2.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz", + "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==", + "dev": true }, "node_modules/data-uri-to-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "devOptional": true, - "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -7540,36 +6675,41 @@ }, "node_modules/decamelize": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "engines": { "node": ">=0.10.0" } }, "node_modules/decode-uri-component": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10" } }, "node_modules/deep-is": { "version": "0.1.4", - "dev": true, - "license": "MIT" + "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/deepmerge": { - "version": "4.2.2", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/default-gateway": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "execa": "^5.0.0" }, @@ -7578,43 +6718,25 @@ } }, "node_modules/defaults": { - "version": "1.0.3", - "license": "MIT", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -7623,15 +6745,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/degenerator": { - "version": "3.0.2", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-3.0.4.tgz", + "integrity": "sha512-Z66uPeBfHZAHVmue3HPfyKu2Q0rC2cRxbTOsvmU/po5fvvcx27W4mIu9n0PUlQih4oUYvcG1BsbtVv8x7KDOSw==", "dev": true, - "license": "MIT", "dependencies": { "ast-types": "^0.13.2", "escodegen": "^1.8.1", "esprima": "^4.0.0", - "vm2": "^3.9.8" + "vm2": "^3.9.17" }, "engines": { "node": ">= 6" @@ -7639,37 +6771,42 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/delegates": { "version": "1.0.0", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "devOptional": true }, "node_modules/depd": { - "version": "1.1.2", - "devOptional": true, - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/dependency-graph": { "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6.0" } }, "node_modules/destroy": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -7677,25 +6814,29 @@ }, "node_modules/detect-node": { "version": "2.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true }, "node_modules/diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/dijkstrajs": { - "version": "1.0.2", - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" }, "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, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -7703,15 +6844,11 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "dev": true, - "license": "MIT" - }, "node_modules/dns-packet": { - "version": "5.4.0", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, - "license": "MIT", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -7721,8 +6858,9 @@ }, "node_modules/dom-serializer": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, - "license": "MIT", "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -7733,27 +6871,30 @@ } }, "node_modules/dom7": { - "version": "4.0.4", - "license": "MIT", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/dom7/-/dom7-4.0.6.tgz", + "integrity": "sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==", "dependencies": { "ssr-window": "^4.0.0" } }, "node_modules/domelementtype": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/fb55" } - ], - "license": "BSD-2-Clause" + ] }, "node_modules/domhandler": { "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.2.0" }, @@ -7765,13 +6906,15 @@ } }, "node_modules/dompurify": { - "version": "2.4.0", - "license": "(MPL-2.0 OR Apache-2.0)" + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.5.tgz", + "integrity": "sha512-FgbqnEPiv5Vdtwt6Mxl7XSylttCC03cqP5ldNT2z+Kj0nLxPHJH4+1Cyf5Jasxhw93Rl4Oo11qRoUV72fmya2Q==" }, "node_modules/domutils": { "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -7783,21 +6926,24 @@ }, "node_modules/duplexer": { "version": "0.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true }, "node_modules/duplexer2": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "readable-stream": "^2.0.2" } }, "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.7", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7808,33 +6954,44 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/duplexer2/node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/ee-first": { "version": "1.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.255", - "dev": true, - "license": "ISC" + "version": "1.4.816", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz", + "integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==", + "dev": true }, "node_modules/elementtree": { "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "sax": "1.1.4" }, @@ -7844,32 +7001,37 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/encode-utf8": { "version": "1.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==" }, "node_modules/encodeurl": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/encoding": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -7877,8 +7039,9 @@ }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -7889,16 +7052,18 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { - "version": "5.10.0", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", + "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -7909,28 +7074,32 @@ }, "node_modules/entities": { "version": "2.2.0", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/err-code": { "version": "2.0.3", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "devOptional": true }, "node_modules/errno": { "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "prr": "~1.0.1" @@ -7941,26 +7110,52 @@ }, "node_modules/error-ex": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, - "node_modules/es-module-lexer": { - "version": "0.9.3", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, - "license": "MIT" + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "peer": true }, "node_modules/es6-promise": { "version": "4.2.8", - "license": "MIT" + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "node_modules/esbuild": { "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "bin": { "esbuild": "bin/esbuild" @@ -7992,13 +7187,46 @@ "esbuild-windows-arm64": "0.15.5" } }, - "node_modules/esbuild-darwin-arm64": { + "node_modules/esbuild-android-64": { "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "cpu": [ + "x64" + ], + "dev": true, "optional": true, "os": [ "darwin" @@ -8007,10 +7235,235 @@ "node": ">=12" } }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/esbuild-wasm": { "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", "dev": true, - "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -8018,31 +7471,83 @@ "node": ">=12" } }, + "node_modules/esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { - "version": "3.1.1", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-html": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true }, "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==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/escodegen": { "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -8062,8 +7567,9 @@ }, "node_modules/escodegen/node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -8071,8 +7577,9 @@ }, "node_modules/eslint-scope": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -8083,8 +7590,9 @@ }, "node_modules/esprima": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, - "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -8095,8 +7603,9 @@ }, "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, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -8106,63 +7615,72 @@ }, "node_modules/esrecurse/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, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estree-walker": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true }, "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, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/etag": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/eventemitter-asyncresource": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true }, "node_modules/eventemitter3": { "version": "4.0.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, "node_modules/events": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.x" } }, "node_modules/execa": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -8181,17 +7699,24 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "devOptional": true + }, "node_modules/express": { - "version": "4.18.1", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.0", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8207,7 +7732,7 @@ "parseurl": "~1.3.3", "path-to-regexp": "0.1.7", "proxy-addr": "~2.0.7", - "qs": "6.10.3", + "qs": "6.11.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.18.0", @@ -8222,36 +7747,26 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "dev": true, - "license": "MIT" - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, - "node_modules/express/node_modules/depd": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/express/node_modules/qs": { - "version": "6.10.3", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -8262,29 +7777,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "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" - } - ], - "license": "MIT" - }, "node_modules/external-editor": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "devOptional": true, - "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -8300,9 +7797,10 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.12", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "devOptional": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -8316,35 +7814,41 @@ }, "node_modules/fast-json-patch": { "version": "3.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "dev": true, - "license": "MIT" + "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", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, "node_modules/fast-safe-stringify": { "version": "2.1.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true }, "node_modules/fastq": { - "version": "1.13.0", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "devOptional": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/faye-websocket": { "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, - "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -8354,8 +7858,9 @@ }, "node_modules/figures": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "devOptional": true, - "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -8368,16 +7873,18 @@ }, "node_modules/file-uri-to-path": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/fill-range": { - "version": "7.0.1", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "devOptional": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -8387,8 +7894,9 @@ }, "node_modules/finalhandler": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -8404,21 +7912,24 @@ }, "node_modules/finalhandler/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/find-cache-dir": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, - "license": "MIT", "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -8433,7 +7944,8 @@ }, "node_modules/find-up": { "version": "4.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -8444,8 +7956,9 @@ }, "node_modules/find-versions": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", + "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", "dev": true, - "license": "MIT", "dependencies": { "semver-regex": "^3.1.2" }, @@ -8457,7 +7970,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -8465,7 +7980,6 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -8477,8 +7991,9 @@ }, "node_modules/form-data": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", "dev": true, - "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -8490,44 +8005,50 @@ }, "node_modules/formidable": { "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", "dev": true, - "license": "MIT", "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } }, "node_modules/forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fraction.js": { - "version": "4.2.0", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, - "license": "MIT", "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fs-extra": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -8540,8 +8061,9 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -8550,19 +8072,23 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "dev": true, - "license": "Unlicense" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", + "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "devOptional": true }, "node_modules/fsevents": { - "version": "2.3.2", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -8573,6 +8099,8 @@ }, "node_modules/ftp": { "version": "0.3.10", + "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha512-faFVML1aBx2UoDStmLwv2Wptt4vw5x03xxX172nhA5Y5HBshW5JweqQ2W4xL4dezQTG8inJsuYcpPHHU3X5OTQ==", "dev": true, "dependencies": { "readable-stream": "1.1.x", @@ -8584,13 +8112,15 @@ }, "node_modules/ftp/node_modules/isarray": { "version": "0.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true }, "node_modules/ftp/node_modules/readable-stream": { "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, - "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -8600,25 +8130,33 @@ }, "node_modules/ftp/node_modules/string_decoder": { "version": "0.10.31", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true }, "node_modules/function-bind": { - "version": "1.1.1", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "devOptional": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/fuse.js": { "version": "6.6.2", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz", + "integrity": "sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA==", "engines": { "node": ">=10" } }, "node_modules/gauge": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", "devOptional": true, - "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -8635,29 +8173,35 @@ }, "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, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", - "license": "ISC", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8665,16 +8209,18 @@ }, "node_modules/get-package-type": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/get-stream": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -8684,8 +8230,9 @@ }, "node_modules/get-uri": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "1", "data-uri-to-buffer": "3", @@ -8700,16 +8247,18 @@ }, "node_modules/get-uri/node_modules/@tootallnate/once": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/get-uri/node_modules/fs-extra": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -8721,24 +8270,28 @@ }, "node_modules/get-uri/node_modules/jsonfile": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/get-uri/node_modules/universalify": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4.0.0" } }, "node_modules/glob": { "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "devOptional": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8755,8 +8308,9 @@ }, "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==", "devOptional": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -8766,25 +8320,28 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "dev": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true }, "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, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/globby": { - "version": "13.1.2", + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", "dev": true, - "license": "MIT", "dependencies": { "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", "merge2": "^1.4.1", "slash": "^4.0.0" }, @@ -8808,14 +8365,16 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "devOptional": true, - "license": "ISC" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "devOptional": true }, "node_modules/gzip-size": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", "dev": true, - "license": "MIT", "dependencies": { "duplexer": "^0.1.2" }, @@ -8828,43 +8387,35 @@ }, "node_modules/handle-thing": { "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/has": { - "version": "1.0.3", - "devOptional": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "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, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -8875,8 +8426,9 @@ }, "node_modules/has-symbols": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8886,12 +8438,14 @@ }, "node_modules/has-unicode": { "version": "2.0.1", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "devOptional": true }, "node_modules/hash-base": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", @@ -8901,28 +8455,23 @@ "node": ">=4" } }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "devOptional": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/hdr-histogram-js": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", "dev": true, - "license": "BSD", "dependencies": { "@assemblyscript/loader": "^0.10.1", "base64-js": "^1.2.0", @@ -8931,26 +8480,30 @@ }, "node_modules/hdr-histogram-js/node_modules/pako": { "version": "1.0.11", - "dev": true, - "license": "(MIT AND Zlib)" + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true }, "node_modules/hdr-histogram-percentiles-obj": { "version": "3.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "license": "MIT", "bin": { "he": "bin/he" } }, "node_modules/hosted-git-info": { - "version": "5.1.0", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", "devOptional": true, - "license": "ISC", "dependencies": { "lru-cache": "^7.5.1" }, @@ -8958,10 +8511,20 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/hpack.js": { "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, - "license": "MIT", "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -8970,9 +8533,10 @@ } }, "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8983,33 +8547,60 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/html-entities": { - "version": "2.3.3", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true }, "node_modules/http-cache-semantics": { "version": "4.1.1", - "devOptional": true, - "license": "BSD-2-Clause" + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "devOptional": true }, "node_modules/http-deceiver": { "version": "1.2.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true }, "node_modules/http-errors": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, - "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -9021,23 +8612,17 @@ "node": ">= 0.8" } }, - "node_modules/http-errors/node_modules/depd": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/http-parser-js": { "version": "0.5.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true }, "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, - "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -9049,8 +8634,9 @@ }, "node_modules/http-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "devOptional": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -9062,8 +8648,9 @@ }, "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, - "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -9085,8 +8672,9 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "devOptional": true, - "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" @@ -9097,25 +8685,28 @@ }, "node_modules/human-signals": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/humanize-ms": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "devOptional": true, - "license": "MIT", "dependencies": { "ms": "^2.0.0" } }, "node_modules/husky": { "version": "4.3.8", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.8.tgz", + "integrity": "sha512-LCqqsB0PzJQ/AlCgfrfzRe3e3+NvmefAdKQhRYpxS4u6clblBoDdzzvHi8fmxKRzvMxPY/1WZWzomPZww0Anow==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "ci-info": "^2.0.0", @@ -9142,8 +8733,9 @@ }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -9156,8 +8748,9 @@ }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -9171,8 +8764,9 @@ }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -9182,13 +8776,15 @@ }, "node_modules/husky/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9202,16 +8798,18 @@ }, "node_modules/husky/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -9224,8 +8822,9 @@ }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -9238,8 +8837,9 @@ }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -9252,8 +8852,9 @@ }, "node_modules/husky/node_modules/pkg-dir": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^5.0.0" }, @@ -9263,16 +8864,18 @@ }, "node_modules/husky/node_modules/slash": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/husky/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, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9282,8 +8885,9 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "devOptional": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -9293,8 +8897,9 @@ }, "node_modules/icss-utils": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, - "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -9304,6 +8909,8 @@ }, "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", @@ -9317,21 +8924,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { - "version": "5.2.0", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/ignore-walk": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", "devOptional": true, - "license": "ISC", "dependencies": { "minimatch": "^5.0.1" }, @@ -9341,8 +8949,9 @@ }, "node_modules/image-size": { "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "dev": true, - "license": "MIT", "optional": true, "bin": { "image-size": "bin/image-size.js" @@ -9352,14 +8961,16 @@ } }, "node_modules/immutable": { - "version": "4.1.0", - "dev": true, - "license": "MIT" + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", + "dev": true }, "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, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -9373,37 +8984,43 @@ }, "node_modules/import-fresh/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, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/infer-owner": { "version": "1.0.4", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "devOptional": true }, "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==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "devOptional": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -9411,28 +9028,32 @@ }, "node_modules/inherits": { "version": "2.0.4", - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", "devOptional": true, - "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/injection-js": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz", + "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^2.0.0" } }, "node_modules/inquirer": { "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", "devOptional": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -9456,8 +9077,9 @@ }, "node_modules/inquirer/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==", "devOptional": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -9470,8 +9092,9 @@ }, "node_modules/inquirer/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "devOptional": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -9485,8 +9108,9 @@ }, "node_modules/inquirer/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==", "devOptional": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -9496,21 +9120,24 @@ }, "node_modules/inquirer/node_modules/color-name": { "version": "1.1.4", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/inquirer/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==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/inquirer/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==", "devOptional": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9519,45 +9146,52 @@ } }, "node_modules/ionicons": { - "version": "6.0.3", - "license": "MIT", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-6.1.3.tgz", + "integrity": "sha512-ptzz38dd/Yq+PgjhXegh7yhb/SLIk1bvL9vQDtLv1aoSc7alO6mX2DIMgcKYzt9vrNWkRu1f9Jr78zIFFyOXqw==", "dependencies": { - "@stencil/core": "~2.16.0" - } - }, - "node_modules/ionicons/node_modules/@stencil/core": { - "version": "2.16.1", - "license": "MIT", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=12.10.0", - "npm": ">=6.0.0" + "@stencil/core": "^2.18.0" } }, "node_modules/ip": { - "version": "2.0.0", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", + "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", + "dev": true + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "devOptional": true, - "license": "MIT" + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } }, "node_modules/ipaddr.js": { - "version": "2.0.1", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/is-arrayish": { "version": "0.2.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "devOptional": true, - "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -9566,9 +9200,10 @@ } }, "node_modules/is-builtin-module": { - "version": "3.2.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", "dev": true, - "license": "MIT", "dependencies": { "builtin-modules": "^3.3.0" }, @@ -9580,11 +9215,15 @@ } }, "node_modules/is-core-module": { - "version": "2.10.0", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", + "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", "devOptional": true, - "license": "MIT", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9592,8 +9231,9 @@ }, "node_modules/is-docker": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "devOptional": true, - "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -9606,23 +9246,26 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { "node": ">=8" } }, "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==", "devOptional": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -9632,33 +9275,38 @@ }, "node_modules/is-interactive": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "engines": { "node": ">=8" } }, "node_modules/is-lambda": { "version": "1.0.1", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "devOptional": true }, "node_modules/is-module": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true }, "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==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, "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, - "license": "MIT", "engines": { "node": ">=10" }, @@ -9668,8 +9316,9 @@ }, "node_modules/is-plain-object": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -9679,8 +9328,9 @@ }, "node_modules/is-stream": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -9690,12 +9340,14 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true }, "node_modules/is-unicode-supported": { "version": "0.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "engines": { "node": ">=10" }, @@ -9705,13 +9357,15 @@ }, "node_modules/is-what": { "version": "3.14.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true }, "node_modules/is-wsl": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "devOptional": true, - "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -9721,34 +9375,39 @@ }, "node_modules/isarray": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true }, "node_modules/isexe": { "version": "2.0.0", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "devOptional": true }, "node_modules/isobject": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.0", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -9761,17 +9420,19 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/jest-worker": { "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -9783,16 +9444,18 @@ }, "node_modules/jest-worker/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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9804,20 +9467,23 @@ } }, "node_modules/jose": { - "version": "4.9.3", - "license": "MIT", + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "funding": { "url": "https://github.com/sponsors/panva" } }, "node_modules/js-tokens": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dependencies": { "argparse": "^2.0.1" }, @@ -9825,10 +9491,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "devOptional": true + }, "node_modules/jsesc": { "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -9838,17 +9511,20 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "devOptional": true }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "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, - "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -9858,12 +9534,14 @@ }, "node_modules/jsonc-parser": { "version": "3.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==" }, "node_modules/jsonfile": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9873,40 +9551,45 @@ }, "node_modules/jsonparse": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "devOptional": true, "engines": [ "node >= 0.2.0" - ], - "license": "MIT" + ] }, "node_modules/karma-source-map-support": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, - "license": "MIT", "dependencies": { "source-map-support": "^0.5.5" } }, "node_modules/kind-of": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/klona": { - "version": "2.0.5", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/leek": { "version": "0.0.24", + "resolved": "https://registry.npmjs.org/leek/-/leek-0.0.24.tgz", + "integrity": "sha512-6PVFIYXxlYF0o6hrAsHtGpTmi06otkwNrMcmQ0K96SeSRHPREPa9J3nJZ1frliVH7XT0XFswoJFQoXsDukzGNQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^2.1.0", "lodash.assign": "^3.2.0", @@ -9915,21 +9598,24 @@ }, "node_modules/leek/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/leek/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/less": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -9953,8 +9639,9 @@ }, "node_modules/less-loader": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", "dev": true, - "license": "MIT", "dependencies": { "klona": "^2.0.4" }, @@ -9972,8 +9659,9 @@ }, "node_modules/less/node_modules/make-dir": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "pify": "^4.0.1", @@ -9998,17 +9686,19 @@ }, "node_modules/less/node_modules/pify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=6" } }, "node_modules/less/node_modules/semver": { - "version": "5.7.1", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -10016,8 +9706,9 @@ }, "node_modules/less/node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -10025,8 +9716,9 @@ }, "node_modules/levn": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -10037,8 +9729,9 @@ }, "node_modules/license-webpack-plugin": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, - "license": "ISC", "dependencies": { "webpack-sources": "^3.0.0" }, @@ -10053,21 +9746,24 @@ }, "node_modules/lilconfig": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/lines-and-columns": { "version": "1.2.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/lint-staged": { "version": "12.5.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.5.0.tgz", + "integrity": "sha512-BKLUjWDsKquV/JuIcoQW4MSAI3ggwEImF1+sB4zaKvyVx1wBk3FsG7UK9bpnmBTN1pm7EH2BBcMwINJzCRv12g==", "dev": true, - "license": "MIT", "dependencies": { "cli-truncate": "^3.1.0", "colorette": "^2.0.16", @@ -10095,9 +9791,10 @@ } }, "node_modules/lint-staged/node_modules/supports-color": { - "version": "9.2.3", + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -10107,8 +9804,9 @@ }, "node_modules/listr2": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", "dev": true, - "license": "MIT", "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", @@ -10133,8 +9831,9 @@ }, "node_modules/listr2/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -10147,8 +9846,9 @@ }, "node_modules/listr2/node_modules/cli-truncate": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -10162,8 +9862,9 @@ }, "node_modules/listr2/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -10173,13 +9874,15 @@ }, "node_modules/listr2/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/listr2/node_modules/slice-ansi": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -10191,23 +9894,26 @@ }, "node_modules/loader-runner": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { - "version": "3.2.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 12.13.0" } }, "node_modules/locate-path": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dependencies": { "p-locate": "^4.1.0" }, @@ -10217,12 +9923,14 @@ }, "node_modules/lodash": { "version": "4.17.21", - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash._baseassign": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha512-t3N26QR2IdSN+gqSy9Ds9pBu/J1EAFEshKlUHpJG3rvyJOYgcELIxcIeKKfZk7sjOz11cFfzJRsyFry/JyabJQ==", "dev": true, - "license": "MIT", "dependencies": { "lodash._basecopy": "^3.0.0", "lodash.keys": "^3.0.0" @@ -10230,18 +9938,21 @@ }, "node_modules/lodash._basecopy": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha512-rFR6Vpm4HeCK1WPGvjZSJ+7yik8d8PVUdCJx5rT2pogG4Ve/2ZS7kfmO5l5T2o5V2mqlNIfSF5MZlr1+xOoYQQ==", + "dev": true }, "node_modules/lodash._bindcallback": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", + "integrity": "sha512-2wlI0JRAGX8WEf4Gm1p/mv/SZ+jLijpj0jyaE/AXeuQphzCgD8ZQW4oSpoN8JAopujOFGU3KMuq7qfHBWlGpjQ==", + "dev": true }, "node_modules/lodash._createassigner": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz", + "integrity": "sha512-LziVL7IDnJjQeeV95Wvhw6G28Z8Q6da87LWKOPWmzBLv4u6FAT/x5v00pyGW0u38UoogNF2JnD3bGgZZDaNEBw==", "dev": true, - "license": "MIT", "dependencies": { "lodash._bindcallback": "^3.0.0", "lodash._isiterateecall": "^3.0.0", @@ -10250,18 +9961,21 @@ }, "node_modules/lodash._getnative": { "version": "3.9.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA==", + "dev": true }, "node_modules/lodash._isiterateecall": { "version": "3.0.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha512-De+ZbrMu6eThFti/CSzhRvTKMgQToLxbij58LMfM8JnYDNSOjkjTCIaa8ixglOeGh2nyPlakbt5bJWJ7gvpYlQ==", + "dev": true }, "node_modules/lodash.assign": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz", + "integrity": "sha512-/VVxzgGBmbphasTg51FrztxQJ/VgAUpol6zmJuSVSGcNg4g7FA4z7rQV8Ovr9V3vFBNWZhvKWHfpAytjTVUfFA==", "dev": true, - "license": "MIT", "dependencies": { "lodash._baseassign": "^3.0.0", "lodash._createassigner": "^3.0.0", @@ -10270,23 +9984,27 @@ }, "node_modules/lodash.debounce": { "version": "4.0.8", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, "node_modules/lodash.isarguments": { "version": "3.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dev": true }, "node_modules/lodash.isarray": { "version": "3.0.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ==", + "dev": true }, "node_modules/lodash.keys": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha512-CuBsapFjcubOGMn3VD+24HOAPxM79tH+V6ivJL3CHYjtrawauDJHUk//Yew9Hvc6e9rbCrURGk8z6PC+8WJBfQ==", "dev": true, - "license": "MIT", "dependencies": { "lodash._getnative": "^3.0.0", "lodash.isarguments": "^3.0.0", @@ -10295,12 +10013,14 @@ }, "node_modules/lodash.restparam": { "version": "3.6.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha512-L4/arjjuq4noiUJpt3yS6KIKDtJwNe2fIYgMqyYYKoeIfV1iEqvPwhCx23o+R9dzouGihDAPN1dTIRWa7zk8tw==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -10314,7 +10034,8 @@ }, "node_modules/log-symbols/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, @@ -10327,7 +10048,8 @@ }, "node_modules/log-symbols/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -10341,7 +10063,8 @@ }, "node_modules/log-symbols/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, @@ -10351,18 +10074,21 @@ }, "node_modules/log-symbols/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/log-symbols/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { "node": ">=8" } }, "node_modules/log-symbols/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { "has-flag": "^4.0.0" }, @@ -10372,8 +10098,9 @@ }, "node_modules/log-update": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", @@ -10389,8 +10116,9 @@ }, "node_modules/log-update/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -10403,8 +10131,9 @@ }, "node_modules/log-update/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -10414,13 +10143,15 @@ }, "node_modules/log-update/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10431,21 +10162,24 @@ } }, "node_modules/long": { - "version": "5.2.0", - "license": "Apache-2.0" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/lru-cache": { - "version": "7.14.0", - "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=12" + "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/macos-release": { - "version": "2.5.0", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -10455,7 +10189,8 @@ }, "node_modules/magic-string": { "version": "0.26.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", "dependencies": { "sourcemap-codec": "^1.4.8" }, @@ -10465,8 +10200,9 @@ }, "node_modules/make-dir": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -10478,22 +10214,25 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/make-error": { "version": "1.3.6", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true }, "node_modules/make-fetch-happen": { "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "devOptional": true, - "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", @@ -10516,9 +10255,19 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "devOptional": true, + "engines": { + "node": ">=12" + } + }, "node_modules/marked": { - "version": "4.1.0", - "license": "MIT", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "bin": { "marked": "bin/marked.js" }, @@ -10528,7 +10277,8 @@ }, "node_modules/md5.js": { "version": "1.3.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -10537,18 +10287,20 @@ }, "node_modules/media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/memfs": { - "version": "3.4.7", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, - "license": "Unlicense", "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -10556,36 +10308,41 @@ }, "node_modules/merge-descriptors": { "version": "1.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true }, "node_modules/merge-stream": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "devOptional": true, - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/methods": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/micromatch": { - "version": "4.0.5", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "devOptional": true, - "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -10608,16 +10365,18 @@ }, "node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -10627,15 +10386,17 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "engines": { "node": ">=6" } }, "node_modules/mini-css-extract-plugin": { "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", "dev": true, - "license": "MIT", "dependencies": { "schema-utils": "^4.0.0" }, @@ -10651,14 +10412,15 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -10670,13 +10432,15 @@ }, "node_modules/minimalistic-assert": { "version": "1.0.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true }, "node_modules/minimatch": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", "devOptional": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -10685,14 +10449,19 @@ } }, "node_modules/minimist": { - "version": "1.2.6", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT" + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minipass": { - "version": "3.3.4", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "devOptional": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -10702,8 +10471,9 @@ }, "node_modules/minipass-collect": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -10713,8 +10483,9 @@ }, "node_modules/minipass-fetch": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "devOptional": true, - "license": "MIT", "dependencies": { "minipass": "^3.1.6", "minipass-sized": "^1.0.3", @@ -10729,8 +10500,9 @@ }, "node_modules/minipass-flush": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -10740,8 +10512,9 @@ }, "node_modules/minipass-json-stream": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", "devOptional": true, - "license": "MIT", "dependencies": { "jsonparse": "^1.3.1", "minipass": "^3.0.0" @@ -10749,8 +10522,9 @@ }, "node_modules/minipass-pipeline": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -10760,8 +10534,9 @@ }, "node_modules/minipass-sized": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -10769,10 +10544,17 @@ "node": ">=8" } }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, "node_modules/minizlib": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "devOptional": true, - "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -10781,10 +10563,17 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, "node_modules/mkdirp": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "devOptional": true, - "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -10794,25 +10583,29 @@ }, "node_modules/monaco-editor": { "version": "0.33.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz", + "integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==" }, "node_modules/mrmime": { - "version": "1.0.1", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/ms": { "version": "2.1.2", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "devOptional": true }, "node_modules/multicast-dns": { "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, - "license": "MIT", "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" @@ -10823,7 +10616,8 @@ }, "node_modules/multimatch": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", "optional": true, "dependencies": { "@types/minimatch": "^3.0.3", @@ -10841,7 +10635,8 @@ }, "node_modules/multimatch/node_modules/brace-expansion": { "version": "1.1.11", - "license": "MIT", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -10850,7 +10645,8 @@ }, "node_modules/multimatch/node_modules/minimatch": { "version": "3.1.2", - "license": "ISC", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -10861,20 +10657,29 @@ }, "node_modules/mustache": { "version": "4.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", "bin": { "mustache": "bin/mustache" } }, "node_modules/mute-stream": { "version": "0.0.8", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "devOptional": true }, "node_modules/nanoid": { - "version": "3.3.4", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, - "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -10883,12 +10688,12 @@ } }, "node_modules/needle": { - "version": "3.1.0", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "debug": "^3.2.6", "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, @@ -10899,19 +10704,11 @@ "node": ">= 4.4.x" } }, - "node_modules/needle/node_modules/debug": { - "version": "3.2.7", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/needle/node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -10921,76 +10718,85 @@ } }, "node_modules/needle/node_modules/sax": { - "version": "1.2.4", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true, - "license": "ISC", "optional": true }, "node_modules/negotiator": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "devOptional": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/neo-async": { "version": "2.6.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, "node_modules/netmask": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/ng-morph": { - "version": "2.1.0", - "license": "Apache-2.0", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/ng-morph/-/ng-morph-4.0.5.tgz", + "integrity": "sha512-5tnlb5WrGKeo2E7VRcV7ZHhScyNgliYqpbXqt103kynmfj6Ic8kzhJAhHu9iLkF1yRnKv2kyCE+O7UGZx5RraQ==", "optional": true, "dependencies": { - "jsonc-parser": "3.0.0", - "minimatch": "3.0.4", + "jsonc-parser": "3.2.0", + "minimatch": "9.0.3", "multimatch": "5.0.0", - "ts-morph": "10.0.2" + "ts-morph": "21.0.1", + "tslib": "2.6.2" }, "peerDependencies": { "@angular-devkit/core": ">=11.0.0", "@angular-devkit/schematics": ">=11.0.0" } }, - "node_modules/ng-morph/node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "optional": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/ng-morph/node_modules/jsonc-parser": { - "version": "3.0.0", - "license": "MIT", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "optional": true }, "node_modules/ng-morph/node_modules/minimatch": { - "version": "3.0.4", - "license": "ISC", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "optional": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ng-morph/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, "node_modules/ng-packagr": { - "version": "14.2.1", + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-14.2.2.tgz", + "integrity": "sha512-AqwHcMM6x+JkCHT++IsbulnTdyoXcC2Cr4tbPamuieacc77+fFbB195hdcqEFwsKX5410cymx/ZUyHird9rxlg==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.1.3", @@ -11034,7 +10840,8 @@ }, "node_modules/ng-qrcode": { "version": "7.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ng-qrcode/-/ng-qrcode-7.0.0.tgz", + "integrity": "sha512-Mx7nf8rtGMVYxGe2qfy8/JNiCnxKD7uFsqpP2Hm5eJSQrOEapQl9FR0yuK0I4MMQorJ7s8mZZDxmszQiH8R2Kg==", "dependencies": { "qrcode": "^1.5.0", "tslib": "^2.4.0" @@ -11046,9 +10853,10 @@ }, "node_modules/nice-napi": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "!win32" @@ -11060,27 +10868,31 @@ }, "node_modules/node-addon-api": { "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/node-forge": { "version": "1.3.1", - "license": "(BSD-3-Clause OR GPL-2.0)", + "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-gyp": { - "version": "9.1.0", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", "devOptional": true, - "license": "MIT", "dependencies": { "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", + "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -11091,13 +10903,14 @@ "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node": "^12.13 || ^14.13 || >=16" } }, "node_modules/node-gyp-build": { - "version": "4.5.0", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, - "license": "MIT", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -11107,8 +10920,9 @@ }, "node_modules/node-gyp/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==", "devOptional": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11116,8 +10930,10 @@ }, "node_modules/node-gyp/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "devOptional": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11135,8 +10951,9 @@ }, "node_modules/node-gyp/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "devOptional": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11146,8 +10963,9 @@ }, "node_modules/node-html-parser": { "version": "5.4.2", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", + "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==", "dev": true, - "license": "MIT", "dependencies": { "css-select": "^4.2.1", "he": "1.2.0" @@ -11155,7 +10973,8 @@ }, "node_modules/node-jose": { "version": "2.2.0", - "license": "Apache-2.0", + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz", + "integrity": "sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==", "dependencies": { "base64url": "^3.0.1", "buffer": "^6.0.3", @@ -11169,35 +10988,43 @@ } }, "node_modules/node-jose/node_modules/uuid": { - "version": "9.0.0", - "license": "MIT", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/node-releases": { - "version": "2.0.6", - "dev": true, - "license": "MIT" + "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/nopt": { - "version": "5.0.0", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "devOptional": true, - "license": "ISC", "dependencies": { - "abbrev": "1" + "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/normalize-package-data": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", "devOptional": true, - "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^5.0.0", "is-core-module": "^2.8.1", @@ -11210,32 +11037,36 @@ }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-range": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/npm-bundled": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "devOptional": true, - "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^1.0.1" } }, "node_modules/npm-install-checks": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", "devOptional": true, - "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, @@ -11245,13 +11076,15 @@ }, "node_modules/npm-normalize-package-bin": { "version": "1.0.1", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "devOptional": true }, "node_modules/npm-package-arg": { "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", "devOptional": true, - "license": "ISC", "dependencies": { "hosted-git-info": "^5.0.0", "proc-log": "^2.0.1", @@ -11264,8 +11097,9 @@ }, "node_modules/npm-packlist": { "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", "devOptional": true, - "license": "ISC", "dependencies": { "glob": "^8.0.1", "ignore-walk": "^5.0.1", @@ -11281,8 +11115,9 @@ }, "node_modules/npm-packlist/node_modules/npm-bundled": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", "devOptional": true, - "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^2.0.0" }, @@ -11292,16 +11127,18 @@ }, "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", "devOptional": true, - "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/npm-pick-manifest": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", "devOptional": true, - "license": "ISC", "dependencies": { "npm-install-checks": "^5.0.0", "npm-normalize-package-bin": "^1.0.1", @@ -11314,8 +11151,9 @@ }, "node_modules/npm-registry-fetch": { "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", "devOptional": true, - "license": "ISC", "dependencies": { "make-fetch-happen": "^10.0.6", "minipass": "^3.1.6", @@ -11331,8 +11169,9 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -11342,8 +11181,10 @@ }, "node_modules/npmlog": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", "devOptional": true, - "license": "ISC", "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -11356,8 +11197,9 @@ }, "node_modules/nth-check": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -11366,31 +11208,10 @@ } }, "node_modules/object-inspect": { - "version": "1.12.2", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, "engines": { "node": ">= 0.4" }, @@ -11400,13 +11221,15 @@ }, "node_modules/obuf": { "version": "1.1.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true }, "node_modules/on-finished": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, - "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -11416,23 +11239,26 @@ }, "node_modules/on-headers": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "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==", "devOptional": true, - "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -11445,8 +11271,9 @@ }, "node_modules/open": { "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", "devOptional": true, - "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -11461,24 +11288,27 @@ }, "node_modules/opencollective-postinstall": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", "dev": true, - "license": "MIT", "bin": { "opencollective-postinstall": "index.js" } }, "node_modules/opener": { "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", "dev": true, - "license": "(WTFPL OR MIT)", "bin": { "opener": "bin/opener-bin.js" } }, "node_modules/optionator": { "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -11493,7 +11323,8 @@ }, "node_modules/ora": { "version": "5.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -11514,7 +11345,8 @@ }, "node_modules/ora/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, @@ -11527,7 +11359,8 @@ }, "node_modules/ora/node_modules/chalk": { "version": "4.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11541,7 +11374,8 @@ }, "node_modules/ora/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, @@ -11551,18 +11385,21 @@ }, "node_modules/ora/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/ora/node_modules/has-flag": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "engines": { "node": ">=8" } }, "node_modules/ora/node_modules/supports-color": { "version": "7.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dependencies": { "has-flag": "^4.0.0" }, @@ -11572,8 +11409,9 @@ }, "node_modules/os-name": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", "dev": true, - "license": "MIT", "dependencies": { "macos-release": "^2.5.0", "windows-release": "^4.0.0" @@ -11587,15 +11425,17 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/p-limit": { "version": "2.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dependencies": { "p-try": "^2.0.0" }, @@ -11608,7 +11448,8 @@ }, "node_modules/p-locate": { "version": "4.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dependencies": { "p-limit": "^2.2.0" }, @@ -11618,8 +11459,9 @@ }, "node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "devOptional": true, - "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" }, @@ -11632,8 +11474,9 @@ }, "node_modules/p-retry": { "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" @@ -11644,23 +11487,26 @@ }, "node_modules/p-retry/node_modules/retry": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/p-try": { "version": "2.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "engines": { "node": ">=6" } }, "node_modules/pac-proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -11678,16 +11524,18 @@ }, "node_modules/pac-proxy-agent/node_modules/@tootallnate/once": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -11699,8 +11547,9 @@ }, "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "4", @@ -11712,8 +11561,9 @@ }, "node_modules/pac-resolver": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", "dev": true, - "license": "MIT", "dependencies": { "degenerator": "^3.0.2", "ip": "^1.1.5", @@ -11723,15 +11573,11 @@ "node": ">= 8" } }, - "node_modules/pac-resolver/node_modules/ip": { - "version": "1.1.8", - "dev": true, - "license": "MIT" - }, "node_modules/pacote": { "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", "devOptional": true, - "license": "ISC", "dependencies": { "@npmcli/git": "^3.0.0", "@npmcli/installed-package-contents": "^1.0.7", @@ -11763,13 +11609,15 @@ } }, "node_modules/pako": { - "version": "2.0.4", - "license": "(MIT AND Zlib)" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" }, "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, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -11779,8 +11627,9 @@ }, "node_modules/parse-json": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -11796,19 +11645,22 @@ }, "node_modules/parse-node-version": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/parse5": { "version": "6.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, "node_modules/parse5-html-rewriting-stream": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", "dependencies": { "parse5": "^6.0.1", "parse5-sax-parser": "^6.0.1" @@ -11816,23 +11668,26 @@ }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "dev": true, - "license": "MIT", "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parse5-sax-parser": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -11843,53 +11698,61 @@ }, "node_modules/path-browserify": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "optional": true }, "node_modules/path-exists": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "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==", "devOptional": true, - "license": "MIT", "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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "devOptional": true }, "node_modules/path-to-regexp": { "version": "0.1.7", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true }, "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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/pbkdf2": { "version": "3.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dependencies": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -11902,14 +11765,16 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -11919,8 +11784,9 @@ }, "node_modules/pidtree": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==", "dev": true, - "license": "MIT", "bin": { "pidtree": "bin/pidtree.js" }, @@ -11930,16 +11796,18 @@ }, "node_modules/pify": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/piscina": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", "dev": true, - "license": "MIT", "dependencies": { "eventemitter-asyncresource": "^1.0.0", "hdr-histogram-js": "^2.0.1", @@ -11951,8 +11819,9 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -11962,21 +11831,25 @@ }, "node_modules/please-upgrade-node": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", "dev": true, - "license": "MIT", "dependencies": { "semver-compare": "^1.0.0" } }, "node_modules/pngjs": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", "engines": { "node": ">=10.13.0" } }, "node_modules/postcss": { - "version": "8.4.16", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "dev": true, "funding": [ { @@ -11986,11 +11859,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -12000,8 +11876,9 @@ }, "node_modules/postcss-attribute-case-insensitive": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", "dev": true, - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -12018,8 +11895,9 @@ }, "node_modules/postcss-clamp": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", "dev": true, - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12032,8 +11910,9 @@ }, "node_modules/postcss-color-functional-notation": { "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12050,8 +11929,9 @@ }, "node_modules/postcss-color-hex-alpha": { "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", "dev": true, - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12068,8 +11948,9 @@ }, "node_modules/postcss-color-rebeccapurple": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12086,8 +11967,9 @@ }, "node_modules/postcss-custom-media": { "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", "dev": true, - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12103,9 +11985,10 @@ } }, "node_modules/postcss-custom-properties": { - "version": "12.1.9", + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", "dev": true, - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12122,8 +12005,9 @@ }, "node_modules/postcss-custom-selectors": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", "dev": true, - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -12140,8 +12024,9 @@ }, "node_modules/postcss-dir-pseudo-class": { "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -12158,8 +12043,9 @@ }, "node_modules/postcss-double-position-gradients": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -12177,8 +12063,9 @@ }, "node_modules/postcss-env-function": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12191,8 +12078,9 @@ }, "node_modules/postcss-focus-visible": { "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -12205,8 +12093,9 @@ }, "node_modules/postcss-focus-within": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.9" }, @@ -12219,16 +12108,18 @@ }, "node_modules/postcss-font-variant": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", "dev": true, - "license": "MIT", "peerDependencies": { "postcss": "^8.1.0" } }, "node_modules/postcss-gap-properties": { "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", "dev": true, - "license": "CC0-1.0", "engines": { "node": "^12 || ^14 || >=16" }, @@ -12242,8 +12133,9 @@ }, "node_modules/postcss-image-set-function": { "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12260,8 +12152,9 @@ }, "node_modules/postcss-import": { "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", "dev": true, - "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -12276,16 +12169,18 @@ }, "node_modules/postcss-initial": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", "dev": true, - "license": "MIT", "peerDependencies": { "postcss": "^8.0.0" } }, "node_modules/postcss-lab-function": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-progressive-custom-properties": "^1.1.0", "postcss-value-parser": "^4.2.0" @@ -12303,8 +12198,9 @@ }, "node_modules/postcss-loader": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", "dev": true, - "license": "MIT", "dependencies": { "cosmiconfig": "^7.0.0", "klona": "^2.0.5", @@ -12324,8 +12220,9 @@ }, "node_modules/postcss-logical": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", "dev": true, - "license": "CC0-1.0", "engines": { "node": "^12 || ^14 || >=16" }, @@ -12335,8 +12232,9 @@ }, "node_modules/postcss-media-minmax": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -12345,9 +12243,10 @@ } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, - "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -12356,9 +12255,10 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dev": true, - "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -12372,9 +12272,10 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.0.0", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dev": true, - "license": "ISC", "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -12387,8 +12288,9 @@ }, "node_modules/postcss-modules-values": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, - "license": "ISC", "dependencies": { "icss-utils": "^5.0.0" }, @@ -12401,8 +12303,9 @@ }, "node_modules/postcss-nesting": { "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" @@ -12419,7 +12322,9 @@ } }, "node_modules/postcss-opacity-percentage": { - "version": "1.1.2", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", "dev": true, "funding": [ { @@ -12431,15 +12336,18 @@ "url": "https://liberapay.com/mrcgrtz" } ], - "license": "MIT", "engines": { "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" } }, "node_modules/postcss-overflow-shorthand": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12456,16 +12364,18 @@ }, "node_modules/postcss-page-break": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", "dev": true, - "license": "MIT", "peerDependencies": { "postcss": "^8" } }, "node_modules/postcss-place": { "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -12482,8 +12392,9 @@ }, "node_modules/postcss-preset-env": { "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", "dev": true, - "license": "CC0-1.0", "dependencies": { "@csstools/postcss-cascade-layers": "^1.0.5", "@csstools/postcss-color-function": "^1.1.1", @@ -12548,8 +12459,9 @@ }, "node_modules/postcss-pseudo-class-any-link": { "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", "dev": true, - "license": "CC0-1.0", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -12566,16 +12478,18 @@ }, "node_modules/postcss-replace-overflow-wrap": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", "dev": true, - "license": "MIT", "peerDependencies": { "postcss": "^8.0.3" } }, "node_modules/postcss-selector-not": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", "dev": true, - "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.10" }, @@ -12591,9 +12505,10 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.10", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", "dev": true, - "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12604,8 +12519,9 @@ }, "node_modules/postcss-url": { "version": "10.1.3", + "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz", + "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==", "dev": true, - "license": "MIT", "dependencies": { "make-dir": "~3.1.0", "mime": "~2.5.2", @@ -12621,8 +12537,9 @@ }, "node_modules/postcss-url/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, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -12630,8 +12547,9 @@ }, "node_modules/postcss-url/node_modules/mime": { "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -12641,8 +12559,9 @@ }, "node_modules/postcss-url/node_modules/minimatch": { "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -12652,20 +12571,24 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true }, "node_modules/prelude-ls": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "2.7.1", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin-prettier.js" }, @@ -12678,8 +12601,9 @@ }, "node_modules/pretty-bytes": { "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -12689,33 +12613,38 @@ }, "node_modules/proc-log": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", "devOptional": true, - "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/process": { "version": "0.11.10", - "license": "MIT", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "engines": { "node": ">= 0.6.0" } }, "node_modules/process-nextick-args": { "version": "2.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true }, "node_modules/promise-inflight": { "version": "1.0.1", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "devOptional": true }, "node_modules/promise-retry": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "devOptional": true, - "license": "MIT", "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" @@ -12726,8 +12655,9 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, - "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -12738,16 +12668,18 @@ }, "node_modules/proxy-addr/node_modules/ipaddr.js": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/proxy-agent": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.0", "debug": "4", @@ -12764,16 +12696,18 @@ }, "node_modules/proxy-agent/node_modules/@tootallnate/once": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/proxy-agent/node_modules/http-proxy-agent": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -12783,18 +12717,11 @@ "node": ">= 6" } }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/proxy-agent/node_modules/socks-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "4", @@ -12804,41 +12731,41 @@ "node": ">= 6" } }, - "node_modules/proxy-agent/node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, "node_modules/proxy-from-env": { "version": "1.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/prr": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/pump": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/punycode": { - "version": "2.1.1", - "license": "MIT", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } }, "node_modules/qrcode": { - "version": "1.5.1", - "license": "MIT", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", + "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", "dependencies": { "dijkstrajs": "^1.0.1", "encode-utf8": "^1.0.3", @@ -12854,7 +12781,8 @@ }, "node_modules/qrcode/node_modules/ansi-styles": { "version": "4.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { "color-convert": "^2.0.1" }, @@ -12867,7 +12795,8 @@ }, "node_modules/qrcode/node_modules/cliui": { "version": "6.0.0", - "license": "ISC", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -12876,7 +12805,8 @@ }, "node_modules/qrcode/node_modules/color-convert": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { "color-name": "~1.1.4" }, @@ -12886,11 +12816,13 @@ }, "node_modules/qrcode/node_modules/color-name": { "version": "1.1.4", - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/qrcode/node_modules/wrap-ansi": { "version": "6.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12902,11 +12834,13 @@ }, "node_modules/qrcode/node_modules/y18n": { "version": "4.0.3", - "license": "ISC" + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, "node_modules/qrcode/node_modules/yargs": { "version": "15.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -12926,7 +12860,8 @@ }, "node_modules/qrcode/node_modules/yargs-parser": { "version": "18.1.3", - "license": "ISC", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -12936,11 +12871,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.2.tgz", + "integrity": "sha512-x+NLUpx9SYrcwXtX7ob1gnkSems4i/mGZX5SlYxwIau6RrUSODO89TR/XDGGpn5RPWSYIB+aSfuSlV5+CmbTBg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -12951,6 +12887,8 @@ }, "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==", "devOptional": true, "funding": [ { @@ -12965,29 +12903,31 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/range-parser": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { - "version": "2.5.1", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, - "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -13000,8 +12940,9 @@ }, "node_modules/raw-loader": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", + "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", "dev": true, - "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "schema-utils": "^3.0.0" @@ -13019,8 +12960,9 @@ }, "node_modules/raw-loader/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, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -13034,21 +12976,24 @@ }, "node_modules/raw-loader/node_modules/ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, "node_modules/raw-loader/node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "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/raw-loader/node_modules/loader-utils": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -13059,9 +13004,10 @@ } }, "node_modules/raw-loader/node_modules/schema-utils": { - "version": "3.1.1", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -13077,16 +13023,19 @@ }, "node_modules/read-cache": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, - "license": "MIT", "dependencies": { "pify": "^2.3.0" } }, "node_modules/read-package-json": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", "devOptional": true, - "license": "ISC", "dependencies": { "glob": "^8.0.1", "json-parse-even-better-errors": "^2.3.1", @@ -13099,8 +13048,9 @@ }, "node_modules/read-package-json-fast": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", "devOptional": true, - "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^2.3.0", "npm-normalize-package-bin": "^1.0.1" @@ -13111,15 +13061,17 @@ }, "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", "devOptional": true, - "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/readable-stream": { - "version": "3.6.0", - "license": "MIT", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -13131,8 +13083,9 @@ }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "devOptional": true, - "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -13141,19 +13094,22 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.13", - "dev": true, - "license": "Apache-2.0" + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "dev": true }, "node_modules/regenerate": { "version": "1.4.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", "dev": true, - "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -13163,47 +13119,47 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true }, "node_modules/regenerator-transform": { - "version": "0.15.0", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.4" } }, "node_modules/regex-parser": { - "version": "2.2.11", - "dev": true, - "license": "MIT" + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true }, "node_modules/regexpu-core": { - "version": "5.2.1", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, - "license": "MIT", "dependencies": { + "@babel/regjsgen": "^0.8.0", "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, - "node_modules/regjsgen": { - "version": "0.7.1", - "dev": true, - "license": "MIT" - }, "node_modules/regjsparser": { "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "jsesc": "~0.5.0" }, @@ -13213,6 +13169,8 @@ }, "node_modules/regjsparser/node_modules/jsesc": { "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, "bin": { "jsesc": "bin/jsesc" @@ -13220,31 +13178,36 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "engines": { "node": ">=0.10.0" } }, "node_modules/require-main-filename": { "version": "2.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "node_modules/requires-port": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true }, "node_modules/resolve": { "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "devOptional": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.9.0", "path-parse": "^1.0.7", @@ -13259,16 +13222,18 @@ }, "node_modules/resolve-from": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/resolve-url-loader": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, - "license": "MIT", "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -13282,8 +13247,9 @@ }, "node_modules/resolve-url-loader/node_modules/loader-utils": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -13295,15 +13261,17 @@ }, "node_modules/resolve-url-loader/node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/restore-cursor": { "version": "3.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -13314,30 +13282,35 @@ }, "node_modules/retry": { "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "devOptional": true, - "license": "MIT", "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==", "devOptional": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rfdc": { - "version": "1.3.0", - "dev": true, - "license": "MIT" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true }, "node_modules/rimraf": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "devOptional": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -13350,8 +13323,9 @@ }, "node_modules/rimraf/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==", "devOptional": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13359,8 +13333,10 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "devOptional": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -13378,8 +13354,9 @@ }, "node_modules/rimraf/node_modules/minimatch": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "devOptional": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13389,16 +13366,18 @@ }, "node_modules/ripemd160": { "version": "2.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "node_modules/rollup": { - "version": "2.79.0", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -13411,8 +13390,9 @@ }, "node_modules/rollup-plugin-sourcemaps": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz", + "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==", "dev": true, - "license": "MIT", "dependencies": { "@rollup/pluginutils": "^3.0.9", "source-map-resolve": "^0.6.0" @@ -13432,22 +13412,26 @@ }, "node_modules/rsvp": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", "dev": true, - "license": "MIT", "engines": { "node": "0.12.* || 4.* || 6.* || >= 7.*" } }, "node_modules/run-async": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, "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==", "devOptional": true, "funding": [ { @@ -13463,7 +13447,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -13477,18 +13460,35 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { "version": "2.1.2", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true }, "node_modules/sass": { "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", "dev": true, - "license": "MIT", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -13503,8 +13503,9 @@ }, "node_modules/sass-loader": { "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", "dev": true, - "license": "MIT", "dependencies": { "klona": "^2.0.4", "neo-async": "^2.6.2" @@ -13540,13 +13541,15 @@ }, "node_modules/sax": { "version": "1.1.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true }, "node_modules/schema-utils": { "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.5", "ajv": "^6.12.4", @@ -13562,8 +13565,9 @@ }, "node_modules/schema-utils/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, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -13577,27 +13581,32 @@ }, "node_modules/schema-utils/node_modules/ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, "node_modules/schema-utils/node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "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/select-hose": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true }, "node_modules/selfsigned": { - "version": "2.1.1", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, - "license": "MIT", "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -13605,9 +13614,10 @@ } }, "node_modules/semver": { - "version": "7.3.7", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "devOptional": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -13620,13 +13630,15 @@ }, "node_modules/semver-compare": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true }, "node_modules/semver-regex": { "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -13636,8 +13648,9 @@ }, "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==", "devOptional": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -13645,10 +13658,17 @@ "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==", + "devOptional": true + }, "node_modules/send": { "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -13670,24 +13690,18 @@ }, "node_modules/send/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, "node_modules/send/node_modules/debug/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/send/node_modules/depd": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/send/node_modules/mime": { "version": "1.6.0", @@ -13703,21 +13717,24 @@ }, "node_modules/send/node_modules/ms": { "version": "2.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/serialize-javascript": { - "version": "6.0.0", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "node_modules/serve-index": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, - "license": "MIT", "dependencies": { "accepts": "~1.3.4", "batch": "0.6.1", @@ -13733,16 +13750,27 @@ }, "node_modules/serve-index/node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "license": "MIT", "dependencies": { "ms": "2.0.0" } }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-index/node_modules/http-errors": { "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, - "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -13755,31 +13783,36 @@ }, "node_modules/serve-index/node_modules/inherits": { "version": "2.0.3", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true }, "node_modules/serve-index/node_modules/ms": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true }, "node_modules/serve-index/node_modules/setprototypeof": { "version": "1.1.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true }, "node_modules/serve-index/node_modules/statuses": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/serve-static": { "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", "dev": true, - "license": "MIT", "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -13792,16 +13825,36 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/setprototypeof": { "version": "1.2.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true }, "node_modules/sha.js": { "version": "2.4.11", - "license": "(MIT AND BSD-3-Clause)", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -13812,8 +13865,9 @@ }, "node_modules/shallow-clone": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, - "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -13823,8 +13877,9 @@ }, "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, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -13834,20 +13889,26 @@ }, "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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/side-channel": { - "version": "1.0.4", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13855,16 +13916,18 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "license": "ISC" + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sirv": { - "version": "1.0.19", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", "dev": true, - "license": "MIT", "dependencies": { - "@polka/url": "^1.0.0-next.20", - "mrmime": "^1.0.0", - "totalist": "^1.0.0" + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" }, "engines": { "node": ">= 10" @@ -13872,8 +13935,9 @@ }, "node_modules/slash": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -13883,8 +13947,9 @@ }, "node_modules/slice-ansi": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -13899,8 +13964,9 @@ }, "node_modules/slice-ansi/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, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -13913,8 +13979,9 @@ }, "node_modules/slice-ansi/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, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -13924,13 +13991,15 @@ }, "node_modules/slice-ansi/node_modules/color-name": { "version": "1.1.4", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/smart-buffer": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "devOptional": true, - "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -13938,8 +14007,9 @@ }, "node_modules/sockjs": { "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, - "license": "MIT", "dependencies": { "faye-websocket": "^0.11.3", "uuid": "^8.3.2", @@ -13947,22 +14017,24 @@ } }, "node_modules/socks": { - "version": "2.7.0", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "devOptional": true, - "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "devOptional": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -13974,23 +14046,26 @@ }, "node_modules/source-map": { "version": "0.7.4", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "engines": { "node": ">= 8" } }, "node_modules/source-map-js": { - "version": "1.0.2", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-loader": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", "dev": true, - "license": "MIT", "dependencies": { "abab": "^2.0.6", "iconv-lite": "^0.6.3", @@ -14009,8 +14084,9 @@ }, "node_modules/source-map-loader/node_modules/iconv-lite": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -14020,8 +14096,10 @@ }, "node_modules/source-map-resolve": { "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", "dev": true, - "license": "MIT", "dependencies": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0" @@ -14029,8 +14107,9 @@ }, "node_modules/source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, - "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -14038,48 +14117,56 @@ }, "node_modules/source-map-support/node_modules/source-map": { "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/sourcemap-codec": { "version": "1.4.8", - "license": "MIT" + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" }, "node_modules/spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "devOptional": true, - "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "devOptional": true, - "license": "CC-BY-3.0" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "devOptional": true }, "node_modules/spdx-expression-parse": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "devOptional": true, - "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "devOptional": true, - "license": "CC0-1.0" + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "devOptional": true }, "node_modules/spdy": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -14093,8 +14180,9 @@ }, "node_modules/spdy-transport": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -14106,30 +14194,35 @@ }, "node_modules/split2": { "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", "dev": true, - "license": "ISC", "dependencies": { "readable-stream": "^3.0.0" } }, "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "devOptional": true }, "node_modules/ssh-config": { "version": "1.1.6", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/ssh-config/-/ssh-config-1.1.6.tgz", + "integrity": "sha512-ZPO9rECxzs5JIQ6G/2EfL1I9ho/BVZkx9HRKn8+0af7QgwAmumQ7XBFP1ggMyPMo+/tUbmv0HFdv4qifdO/9JA==", + "dev": true }, "node_modules/ssr-window": { "version": "4.0.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz", + "integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==" }, "node_modules/ssri": { "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "devOptional": true, - "license": "ISC", "dependencies": { "minipass": "^3.1.1" }, @@ -14139,25 +14232,28 @@ }, "node_modules/statuses": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/stream-combiner2": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", "dev": true, - "license": "MIT", "dependencies": { "duplexer2": "~0.1.0", "readable-stream": "^2.0.2" } }, "node_modules/stream-combiner2/node_modules/readable-stream": { - "version": "2.3.7", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -14168,50 +14264,42 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/stream-combiner2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/stream-combiner2/node_modules/string_decoder": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string_decoder": { "version": "1.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dependencies": { "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/string-argv": { - "version": "0.3.1", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6.19" } }, "node_modules/string-width": { "version": "4.2.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14223,7 +14311,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14233,16 +14322,18 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/stylus": { "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", "dev": true, - "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.0.1", "debug": "^4.3.2", @@ -14262,8 +14353,9 @@ }, "node_modules/stylus-loader": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "^3.2.11", "klona": "^2.0.5", @@ -14283,8 +14375,9 @@ }, "node_modules/stylus/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, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -14292,8 +14385,10 @@ }, "node_modules/stylus/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -14311,8 +14406,9 @@ }, "node_modules/stylus/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, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -14322,13 +14418,16 @@ }, "node_modules/stylus/node_modules/sax": { "version": "1.2.4", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true }, "node_modules/superagent": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", + "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", "dev": true, - "license": "MIT", "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.2", @@ -14348,8 +14447,9 @@ }, "node_modules/superagent-proxy": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/superagent-proxy/-/superagent-proxy-3.0.0.tgz", + "integrity": "sha512-wAlRInOeDFyd9pyonrkJspdRAxdLrcsZ6aSnS+8+nu4x1aXbz6FWSTT9M6Ibze+eG60szlL7JA8wEIV7bPWuyQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.3.2", "proxy-agent": "^5.0.0" @@ -14363,8 +14463,9 @@ }, "node_modules/superagent/node_modules/mime": { "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -14374,8 +14475,9 @@ }, "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, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -14385,8 +14487,9 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "devOptional": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -14395,7 +14498,9 @@ } }, "node_modules/swiper": { - "version": "8.4.2", + "version": "8.4.7", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-8.4.7.tgz", + "integrity": "sha512-VwO/KU3i9IV2Sf+W2NqyzwWob4yX9Qdedq6vBtS0rFqJ6Fa5iLUJwxQkuD4I38w0WDJwmFl8ojkdcRFPHWD+2g==", "funding": [ { "type": "patreon", @@ -14407,7 +14512,6 @@ } ], "hasInstallScript": true, - "license": "MIT", "dependencies": { "dom7": "^4.0.4", "ssr-window": "^4.0.2" @@ -14418,40 +14522,59 @@ }, "node_modules/symbol-observable": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "devOptional": true, - "license": "MIT", "engines": { "node": ">=0.10" } }, "node_modules/tapable": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/tar": { - "version": "6.1.11", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "devOptional": true, - "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, "node_modules/terser": { "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", @@ -14466,15 +14589,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -14500,8 +14624,9 @@ }, "node_modules/terser-webpack-plugin/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, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14515,21 +14640,30 @@ }, "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, + "node_modules/terser-webpack-plugin/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { "version": "0.4.1", - "dev": true, - "license": "MIT" + "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/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -14543,15 +14677,35 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.31.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.1.tgz", + "integrity": "sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/test-exclude": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -14563,8 +14717,9 @@ }, "node_modules/test-exclude/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, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -14572,8 +14727,10 @@ }, "node_modules/test-exclude/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -14591,8 +14748,9 @@ }, "node_modules/test-exclude/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, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -14602,27 +14760,32 @@ }, "node_modules/text-mask-core": { "version": "5.1.2", - "license": "Unlicense" + "resolved": "https://registry.npmjs.org/text-mask-core/-/text-mask-core-5.1.2.tgz", + "integrity": "sha512-VfkCMdmRRZqXgQZFlDMiavm3hzsMzBM23CxHZsaeAYg66ZhXCNJWrFmnJwNy8KF9f74YvAUAuQenxsMCfuvhUw==" }, "node_modules/text-table": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true }, "node_modules/through": { "version": "2.3.8", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "devOptional": true }, "node_modules/thunky": { "version": "1.1.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true }, "node_modules/tmp": { "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "devOptional": true, - "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" }, @@ -14632,16 +14795,18 @@ }, "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, - "license": "MIT", "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==", "devOptional": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -14651,24 +14816,27 @@ }, "node_modules/toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/totalist": { - "version": "1.1.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/tree-kill": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, - "license": "MIT", "bin": { "tree-kill": "cli.js" } @@ -14679,18 +14847,20 @@ "integrity": "sha512-UFYaKgfqlg9FROK7bdpYqFwG1CJvP4kOJdjXuWoqxo9jCmANoDw1GxkSCpJgoTeIiSTaTH5Qr1klSspb8c+ydg==" }, "node_modules/ts-morph": { - "version": "10.0.2", - "license": "MIT", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-21.0.1.tgz", + "integrity": "sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==", "optional": true, "dependencies": { - "@ts-morph/common": "~0.9.0", - "code-block-writer": "^10.1.1" + "@ts-morph/common": "~0.22.0", + "code-block-writer": "^12.0.0" } }, "node_modules/ts-node": { - "version": "10.9.1", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14730,13 +14900,16 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "license": "0BSD" + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/tslint": { "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", "dev": true, - "license": "Apache-2.0", "dependencies": { "@babel/code-frame": "^7.0.0", "builtin-modules": "^1.1.1", @@ -14764,16 +14937,18 @@ }, "node_modules/tslint/node_modules/argparse": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/tslint/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, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -14781,21 +14956,25 @@ }, "node_modules/tslint/node_modules/builtin-modules": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/tslint/node_modules/commander": { "version": "2.20.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/tslint/node_modules/glob": { "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -14813,8 +14992,9 @@ }, "node_modules/tslint/node_modules/js-yaml": { "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -14825,8 +15005,9 @@ }, "node_modules/tslint/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, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -14836,8 +15017,9 @@ }, "node_modules/tslint/node_modules/mkdirp": { "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -14846,22 +15028,31 @@ } }, "node_modules/tslint/node_modules/semver": { - "version": "5.7.1", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver" } }, + "node_modules/tslint/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/tslint/node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/tsutils": { "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, - "license": "MIT", "dependencies": { "tslib": "^1.8.1" }, @@ -14871,13 +15062,15 @@ }, "node_modules/tsutils/node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/type-check": { "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2" }, @@ -14887,8 +15080,9 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "devOptional": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -14898,8 +15092,9 @@ }, "node_modules/type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, - "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -14910,21 +15105,24 @@ }, "node_modules/typed-assert": { "version": "1.0.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } }, "node_modules/typescript": { "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14935,16 +15133,18 @@ }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -14954,63 +15154,72 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unique-filename": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "devOptional": true, - "license": "ISC", "dependencies": { "unique-slug": "^2.0.0" } }, "node_modules/unique-slug": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "devOptional": true, - "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" } }, "node_modules/universalify": { - "version": "2.0.0", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/untildify": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/update-browserslist-db": { - "version": "1.0.9", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -15020,15 +15229,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -15036,39 +15248,45 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/util-deprecate": { "version": "1.0.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/uuid": { "version": "8.3.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, "node_modules/validate-npm-package-license": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "devOptional": true, - "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -15076,8 +15294,9 @@ }, "node_modules/validate-npm-package-name": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", "devOptional": true, - "license": "ISC", "dependencies": { "builtins": "^5.0.0" }, @@ -15087,16 +15306,19 @@ }, "node_modules/vary": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/vm2": { - "version": "3.9.11", + "version": "3.9.19", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", + "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", + "deprecated": "The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm.", "dev": true, - "license": "MIT", "dependencies": { "acorn": "^8.7.0", "acorn-walk": "^8.2.0" @@ -15109,9 +15331,10 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -15122,47 +15345,51 @@ }, "node_modules/wbuf": { "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, - "license": "MIT", "dependencies": { "minimalistic-assert": "^1.0.0" } }, "node_modules/wcwidth": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dependencies": { "defaults": "^1.0.3" } }, "node_modules/webpack": { - "version": "5.74.0", + "version": "5.92.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", + "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.17.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -15182,19 +15409,22 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.8.0", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dev": true, - "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", "acorn-walk": "^8.0.0", - "chalk": "^4.1.0", "commander": "^7.2.0", + "debounce": "^1.2.1", + "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", - "lodash": "^4.17.20", + "html-escaper": "^2.0.2", "opener": "^1.5.2", - "sirv": "^1.0.7", + "picocolors": "^1.0.0", + "sirv": "^2.0.3", "ws": "^7.3.1" }, "bin": { @@ -15204,102 +15434,32 @@ "node": ">= 10.13.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "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/webpack-bundle-analyzer/node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, "node_modules/webpack-bundle-analyzer/node_modules/commander": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10" } }, - "node_modules/webpack-bundle-analyzer/node_modules/has-flag": { + "node_modules/webpack-bundle-analyzer/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, - "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "node": ">=10" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "7.5.9", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/webpack-dev-middleware": { "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", "dev": true, - "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", @@ -15319,14 +15479,15 @@ } }, "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -15338,8 +15499,9 @@ }, "node_modules/webpack-dev-server": { "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", "dev": true, - "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -15391,14 +15553,15 @@ } }, "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, - "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 12.13.0" @@ -15408,10 +15571,32 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/webpack-merge": { "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, - "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "wildcard": "^2.0.0" @@ -15422,16 +15607,18 @@ }, "node_modules/webpack-sources": { "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/webpack-subresource-integrity": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, - "license": "MIT", "dependencies": { "typed-assert": "^1.0.8" }, @@ -15448,10 +15635,19 @@ } } }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "peer": true + }, "node_modules/webpack/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, - "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -15465,21 +15661,27 @@ }, "node_modules/webpack/node_modules/ajv-keywords": { "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", + "peer": true, "peerDependencies": { "ajv": "^6.9.1" } }, "node_modules/webpack/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, - "license": "MIT" + "peer": true }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -15495,8 +15697,9 @@ }, "node_modules/websocket-driver": { "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -15508,16 +15711,18 @@ }, "node_modules/websocket-extensions": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } }, "node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "devOptional": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -15529,34 +15734,39 @@ } }, "node_modules/which-module": { - "version": "2.0.0", - "license": "ISC" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, "node_modules/which-pm-runs": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", + "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/wide-align": { "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "devOptional": true, - "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "node_modules/wildcard": { - "version": "2.0.0", - "dev": true, - "license": "MIT" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true }, "node_modules/windows-release": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", "dev": true, - "license": "MIT", "dependencies": { "execa": "^4.0.2" }, @@ -15569,8 +15779,9 @@ }, "node_modules/windows-release/node_modules/execa": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.0", "get-stream": "^5.0.0", @@ -15591,8 +15802,9 @@ }, "node_modules/windows-release/node_modules/get-stream": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -15605,24 +15817,27 @@ }, "node_modules/windows-release/node_modules/human-signals": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=8.12.0" } }, "node_modules/word-wrap": { - "version": "1.2.3", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "devOptional": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15637,8 +15852,9 @@ }, "node_modules/wrap-ansi/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==", "devOptional": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -15651,8 +15867,9 @@ }, "node_modules/wrap-ansi/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==", "devOptional": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -15662,18 +15879,21 @@ }, "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", - "devOptional": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true }, "node_modules/wrappy": { "version": "1.0.2", - "devOptional": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "devOptional": true }, "node_modules/write-file-atomic": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, - "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -15682,11 +15902,12 @@ } }, "node_modules/ws": { - "version": "8.8.1", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=8.3.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -15703,42 +15924,51 @@ }, "node_modules/xregexp": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha512-xl/50/Cf32VsGq/1R8jJE5ajH1yMCQkpmoS10QbFZWl2Oor4H0Me64Pu2yxvsRWK3m6soJbmGfzSR7BYmDcWAA==", "dev": true, - "license": "MIT" + "engines": { + "node": "*" + } }, "node_modules/xxhashjs": { "version": "0.2.2", + "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", + "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", "dev": true, - "license": "MIT", "dependencies": { "cuint": "^0.2.2" } }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "devOptional": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { - "version": "4.0.0", - "devOptional": true, - "license": "ISC" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "dev": true, - "license": "ISC", "engines": { "node": ">= 6" } }, "node_modules/yargs": { "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", "devOptional": true, - "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -15754,24 +15984,27 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "devOptional": true, - "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yn": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "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, - "license": "MIT", "engines": { "node": ">=10" }, @@ -15781,7 +16014,8 @@ }, "node_modules/zone.js": { "version": "0.11.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz", + "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==", "dependencies": { "tslib": "^2.3.0" } diff --git a/web/package.json b/web/package.json index 1db0dead4..e159a2ee5 100644 --- a/web/package.json +++ b/web/package.json @@ -43,20 +43,22 @@ "@angular/service-worker": "^14.2.2", "@ionic/angular": "^6.1.15", "@materia-ui/ngx-monaco-editor": "^6.0.0", - "@ng-web-apis/common": "^2.0.0", - "@ng-web-apis/mutation-observer": "^2.0.0", - "@ng-web-apis/resize-observer": "^2.0.0", + "@ng-web-apis/common": "^3.0.6", + "@ng-web-apis/mutation-observer": "^3.2.1", + "@ng-web-apis/resize-observer": "^3.2.1", "@noble/curves": "^1.4.0", "@noble/hashes": "^1.4.0", "@start9labs/argon2": "^0.2.2", "@start9labs/emver": "^0.1.5", "@start9labs/start-sdk": "file:../sdk/dist", - "@taiga-ui/addon-charts": "3.20.0", - "@taiga-ui/cdk": "3.20.0", - "@taiga-ui/core": "3.20.0", - "@taiga-ui/icons": "3.20.0", - "@taiga-ui/kit": "3.20.0", + "@taiga-ui/addon-charts": "3.84.0", + "@taiga-ui/cdk": "3.84.0", + "@taiga-ui/core": "3.84.0", + "@taiga-ui/experimental": "3.84.0", + "@taiga-ui/icons": "3.84.0", + "@taiga-ui/kit": "3.84.0", "@tinkoff/ng-dompurify": "4.0.0", + "@tinkoff/ng-event-plugins": "3.2.0", "angular-svg-round-progressbar": "^9.0.0", "ansi-to-html": "^0.7.2", "base64-js": "^1.5.1", diff --git a/web/projects/setup-wizard/src/app/app.component.ts b/web/projects/setup-wizard/src/app/app.component.ts index e10d672e5..e07df5870 100644 --- a/web/projects/setup-wizard/src/app/app.component.ts +++ b/web/projects/setup-wizard/src/app/app.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core' import { NavController } from '@ionic/angular' +import { ErrorService } from '@start9labs/shared' import { ApiService } from './services/api/api.service' -import { ErrorToastService } from '@start9labs/shared' @Component({ selector: 'app-root', @@ -11,7 +11,7 @@ import { ErrorToastService } from '@start9labs/shared' export class AppComponent { constructor( private readonly apiService: ApiService, - private readonly errorToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly navCtrl: NavController, ) {} @@ -26,7 +26,7 @@ export class AppComponent { await this.navCtrl.navigateForward(route) } catch (e: any) { - this.errorToastService.present(e) + this.errorService.handleError(e) } } } diff --git a/web/projects/setup-wizard/src/app/app.module.ts b/web/projects/setup-wizard/src/app/app.module.ts index f2ba59012..973ce35ee 100644 --- a/web/projects/setup-wizard/src/app/app.module.ts +++ b/web/projects/setup-wizard/src/app/app.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { RouteReuseStrategy } from '@angular/router' import { HttpClientModule } from '@angular/common/http' -import { TuiRootModule } from '@taiga-ui/core' +import { TuiAlertModule, TuiRootModule } from '@taiga-ui/core' import { ApiService } from './services/api/api.service' import { MockApiService } from './services/api/mock-api.service' import { LiveApiService } from './services/api/live-api.service' @@ -41,6 +41,7 @@ const { RecoverPageModule, TransferPageModule, TuiRootModule, + TuiAlertModule, ], providers: [ { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, diff --git a/web/projects/setup-wizard/src/app/pages/attach/attach.page.ts b/web/projects/setup-wizard/src/app/pages/attach/attach.page.ts index b4d6eb9f9..3b8323798 100644 --- a/web/projects/setup-wizard/src/app/pages/attach/attach.page.ts +++ b/web/projects/setup-wizard/src/app/pages/attach/attach.page.ts @@ -4,10 +4,10 @@ import { ModalController, NavController, } from '@ionic/angular' -import { ApiService } from 'src/app/services/api/api.service' -import { DiskInfo, ErrorToastService } from '@start9labs/shared' -import { StateService } from 'src/app/services/state.service' +import { DiskInfo, ErrorService } from '@start9labs/shared' import { PasswordPage } from 'src/app/modals/password/password.page' +import { ApiService } from 'src/app/services/api/api.service' +import { StateService } from 'src/app/services/state.service' @Component({ selector: 'app-attach', @@ -21,7 +21,7 @@ export class AttachPage { constructor( private readonly apiService: ApiService, private readonly navCtrl: NavController, - private readonly errToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly stateService: StateService, private readonly modalCtrl: ModalController, private readonly loadingCtrl: LoadingController, @@ -41,7 +41,7 @@ export class AttachPage { try { this.drives = await this.apiService.getDrives() } catch (e: any) { - this.errToastService.present(e) + this.errorService.handleError(e) } finally { this.loading = false } @@ -70,7 +70,7 @@ export class AttachPage { await this.stateService.importDrive(guid, password) await this.navCtrl.navigateForward(`/loading`) } catch (e: any) { - this.errToastService.present(e) + this.errorService.handleError(e) } finally { loader.dismiss() } diff --git a/web/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts b/web/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts index 7f7ee6241..110768be5 100644 --- a/web/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts +++ b/web/projects/setup-wizard/src/app/pages/embassy/embassy.page.ts @@ -5,8 +5,8 @@ import { ModalController, NavController, } from '@ionic/angular' +import { DiskInfo, ErrorService, GuidPipe } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/api.service' -import { DiskInfo, ErrorToastService, GuidPipe } from '@start9labs/shared' import { StateService } from 'src/app/services/state.service' import { PasswordPage } from '../../modals/password/password.page' @@ -27,7 +27,7 @@ export class EmbassyPage { private readonly alertCtrl: AlertController, private readonly stateService: StateService, private readonly loadingCtrl: LoadingController, - private readonly errorToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly guidPipe: GuidPipe, ) {} @@ -71,7 +71,7 @@ export class EmbassyPage { }) } } catch (e: any) { - this.errorToastService.present(e) + this.errorService.handleError(e) } finally { this.loading = false } @@ -148,7 +148,7 @@ export class EmbassyPage { await this.stateService.setupEmbassy(logicalname, password) await this.navCtrl.navigateForward(`/loading`) } catch (e: any) { - this.errorToastService.present(e) + this.errorService.handleError(e) } finally { loader.dismiss() } diff --git a/web/projects/setup-wizard/src/app/pages/home/home.page.ts b/web/projects/setup-wizard/src/app/pages/home/home.page.ts index c0e93d18a..f3d165940 100644 --- a/web/projects/setup-wizard/src/app/pages/home/home.page.ts +++ b/web/projects/setup-wizard/src/app/pages/home/home.page.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core' import { IonicSlides } from '@ionic/angular' +import { ErrorService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/api.service' -import SwiperCore, { Swiper } from 'swiper' -import { ErrorToastService } from '@start9labs/shared' import { StateService } from 'src/app/services/state.service' +import SwiperCore, { Swiper } from 'swiper' SwiperCore.use([IonicSlides]) @@ -19,7 +19,7 @@ export class HomePage { constructor( private readonly api: ApiService, - private readonly errToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly stateService: StateService, ) {} @@ -33,7 +33,7 @@ export class HomePage { await this.api.getPubKey() } catch (e: any) { this.error = true - this.errToastService.present(e) + this.errorService.handleError(e) } finally { this.loading = false } diff --git a/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts b/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts index 459be5c7a..2386c09ec 100644 --- a/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts +++ b/web/projects/setup-wizard/src/app/pages/loading/loading.page.ts @@ -1,14 +1,15 @@ import { Component } from '@angular/core' import { NavController } from '@ionic/angular' -import { Pipe, PipeTransform } from '@angular/core' +import { ErrorService } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' import { - EMPTY, - Observable, catchError, + EMPTY, filter, from, interval, map, + Observable, of, startWith, switchMap, @@ -16,8 +17,6 @@ import { tap, } from 'rxjs' import { ApiService } from 'src/app/services/api/api.service' -import { ErrorToastService } from '@start9labs/shared' -import { T } from '@start9labs/start-sdk' @Component({ selector: 'app-loading', @@ -69,7 +68,7 @@ export class LoadingPage { constructor( private readonly navCtrl: NavController, private readonly api: ApiService, - private readonly errorToastService: ErrorToastService, + private readonly errorService: ErrorService, ) {} private async getStatus(): Promise<{ @@ -96,7 +95,7 @@ export class LoadingPage { return from(this.getStatus()).pipe( filter(Boolean), catchError(e => { - this.errorToastService.present(e) + this.errorService.handleError(e) return of(e) }), take(1), diff --git a/web/projects/setup-wizard/src/app/pages/recover/recover.page.ts b/web/projects/setup-wizard/src/app/pages/recover/recover.page.ts index f9e5adbb1..d47105f24 100644 --- a/web/projects/setup-wizard/src/app/pages/recover/recover.page.ts +++ b/web/projects/setup-wizard/src/app/pages/recover/recover.page.ts @@ -1,8 +1,8 @@ import { Component, Input } from '@angular/core' import { ModalController, NavController } from '@ionic/angular' +import { ErrorService } from '@start9labs/shared' import { CifsModal } from 'src/app/modals/cifs-modal/cifs-modal.page' import { ApiService, DiskBackupTarget } from 'src/app/services/api/api.service' -import { ErrorToastService } from '@start9labs/shared' import { StateService } from 'src/app/services/state.service' import { PasswordPage } from '../../modals/password/password.page' @@ -20,7 +20,7 @@ export class RecoverPage { private readonly navCtrl: NavController, private readonly modalCtrl: ModalController, private readonly modalController: ModalController, - private readonly errToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly stateService: StateService, ) {} @@ -62,7 +62,7 @@ export class RecoverPage { }) }) } catch (e: any) { - this.errToastService.present(e) + this.errorService.handleError(e) } finally { this.loading = false } diff --git a/web/projects/setup-wizard/src/app/pages/success/success.page.ts b/web/projects/setup-wizard/src/app/pages/success/success.page.ts index 71566580a..88262bdf2 100644 --- a/web/projects/setup-wizard/src/app/pages/success/success.page.ts +++ b/web/projects/setup-wizard/src/app/pages/success/success.page.ts @@ -1,6 +1,6 @@ import { DOCUMENT } from '@angular/common' import { Component, ElementRef, Inject, NgZone, ViewChild } from '@angular/core' -import { DownloadHTMLService, ErrorToastService } from '@start9labs/shared' +import { DownloadHTMLService, ErrorService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/api.service' import { StateService } from 'src/app/services/state.service' @@ -12,7 +12,8 @@ import { StateService } from 'src/app/services/state.service' }) export class SuccessPage { @ViewChild('canvas', { static: true }) - private canvas: ElementRef = {} as ElementRef + private canvas: ElementRef = + {} as ElementRef private ctx: CanvasRenderingContext2D = {} as CanvasRenderingContext2D torAddress?: string @@ -28,7 +29,7 @@ export class SuccessPage { constructor( @Inject(DOCUMENT) private readonly document: Document, - private readonly errCtrl: ErrorToastService, + private readonly errorService: ErrorService, private readonly stateService: StateService, private readonly api: ApiService, private readonly downloadHtml: DownloadHTMLService, @@ -83,7 +84,7 @@ export class SuccessPage { await this.api.exit() } } catch (e: any) { - await this.errCtrl.present(e) + await this.errorService.handleError(e) } } diff --git a/web/projects/setup-wizard/src/app/pages/transfer/transfer.page.ts b/web/projects/setup-wizard/src/app/pages/transfer/transfer.page.ts index 5de21a289..5034a2272 100644 --- a/web/projects/setup-wizard/src/app/pages/transfer/transfer.page.ts +++ b/web/projects/setup-wizard/src/app/pages/transfer/transfer.page.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core' import { AlertController, NavController } from '@ionic/angular' +import { DiskInfo, ErrorService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/api.service' -import { DiskInfo, ErrorToastService } from '@start9labs/shared' import { StateService } from 'src/app/services/state.service' @Component({ @@ -17,7 +17,7 @@ export class TransferPage { private readonly apiService: ApiService, private readonly navCtrl: NavController, private readonly alertCtrl: AlertController, - private readonly errToastService: ErrorToastService, + private readonly errorService: ErrorService, private readonly stateService: StateService, ) {} @@ -35,7 +35,7 @@ export class TransferPage { try { this.drives = await this.apiService.getDrives() } catch (e: any) { - this.errToastService.present(e) + this.errorService.handleError(e) } finally { this.loading = false } diff --git a/web/projects/shared/assets/img/icon_transparent.png b/web/projects/shared/assets/img/icon_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..f0aafd15de4c965ad7238b6d89dde4c1124c986f GIT binary patch literal 53685 zcmb5Vc|6qX_W=IP*vA?PMbbicku|$p*%QVxwsDay%atVxgV0TjVkqm#HZjA*2uVg- zELkE$3Zs%OC5AG#-{XG1zt`{YU$0lU*PZ7)=Q;a1&pFTgUpw2g{HXmX006&*`Cs+` zaJcUN<>rLHMEmku!hd+e&0Q}6z$>!*7Xk7L#Nm&KOZI0?K~10J9Q+@4e-j%M03P4r z-Sp-F;GLz#UnY*15#Ps?q7J-i;AHeDA9|?3?QW8dLONqBknN)8HM9th3Pm;dq-j*L zqtJ8spF@Kj#$jg;V)j}n?V};@dt9B=)ywv2jS;@__3h@RnF(%q&i|i33|=t=09RN` zQypL5B}Td?gs}Prrk-rZU0h$1_I!S|nGb;3q)bu3c&pF-m+Hvojf%kSxSbiJgv~#H z`i-`?T$Fm=sG5#tWMrJ%Sr2MpO_nC^EcM5;G?Lu_Zsn*i2M{Oj%`xOVBPW=U7%P1* z2gkIy-rcVJ#Y8ASdF&&X%%a$i%FZQ}e;6CUwF}oFf#X+`aJ_%I+5-h_9Iz}k>q2&) z`kVbF-|Mc`C2~HN$>adUoLz# zWXv+A-qy-U%KVVCjPdlJoavDemfmLDpCh=|E@=nuD^Od^Go2_f@Giyjy=0zYjcFk} zUr%vKFpqcc?>5I5NO18iTwbDe9MViBCR$0WI)GILL!+7WOTXlCtxZi$@{8QdM!Z-A zD3XE9_VuMq*$@5kA--ofm=hr`&t--s0*(eLUN6Ah&}z@nEA=r-4@( z5@4~{NWi$klAsqqprN@ls3H1#(PB6lUTH7F{aes2WL3k7fhL zesB-%ok5jdVVbe%Xs=j%^$lmhaNPaXBz9R)(lu0*;zD*RW<8t#g#wZ(P|UM7yEL)n z&241f7!AG&I#RWM7tg5LKAk?cD z%>Sc`#+9_xMsF!Iqd7qIAt-EXhFz3-0bE*>?-^JsQLSP6JvS zZjV*T{L%O(ORk;7vO7aURY=L6&tKl$`Him00N{)b#B#(Q1KjcZfU}Gv2hpS^sJA4z zCZe}wK^@@ELzAJ=ruPAP$1Pc@iItU|Kw#H0okB;lnvpbaZdU{rO9eo@Fdbc^jR1VN zp@29UCJjNM2sIw?+7hn__%h*d3Xzj;no`s@p5ZDm^Zj}EYO7}ruv3LZJ3e%*{+5Iu z8gLWERYu?!Hr%K4kRzu77#4y@^8~U1+U-y)2J7da(r&aH0_e!VO_J~858&({=cS-S zpf{R@DlS=VzIziT0chuWToDYKB}Gqh3+f!m^y-gGBAY2IQW!3`aBxf`9Osffnvy>>{09=QmyFkLtp)}I>p4s?iuCs2<#O}JjrEZNj&#dyjGu*#5BeY@o0>PX^Msbn;0 zidp&f$AswW7fd zHJkKC?Gh;f-v5yGNZZ~!#skz2Llm65D8!#cn&^3votg`2hXIzhyXA(A6Hv2E^Fycw z>_V9gD}Vy7VKU&`r6uSQh67h$@l+Jo4wpYMOQp#kCO3c#0!s{@u;2{5orRs&x>67;$9xbb%(u}9$9=eV5_z)<7~lJYNHHYyK~f~StW zKm%OmLFyhCcAyU}k74M(WtduB4@P@K)>j9Xe-&FGUWHuDMBdzK*j#WA+<7=Xd6wmx zqxx@A%kez@%^eYyEZn$qcP8mHFos^-hk=GDzx2m&D|$ao7n5_R7&*vXVGJ2nX0E6h zrdKrfZ++O9ti*Zy1n9A^*FD-?^ZpaJKI>eD`(3;4gK|E&vu*)qW@WeZ|HyAC6>tH6 z7+Av7u2rj9{+^myHgRFKrSGh#Ox}@|B-`)=OQ+WtZ`ZpgxxH|0SGw6>=lG+S<$;4az-ruW1n&~JiyaLME-?#}i zjCq}lm|t#Josv(ClfBv7O5I@*)SZNQqxL*O7D&h_(ysG3Pkqh#rTf>Gmp=D-4kDAx z{#f_o)`|~nY{QM)dn)@H8TgxaqSQ=ZpmnPJHc$Nd>U}*V(^~r{+bwQYl|HhU~QCyuE%#Q^Dt#HK<1J6EK4y_PZcLHKt)XznT?R zD>gAz=FcXwB|T-)+9AsJKd+(uI$*LVW`^T6a@mFx(~U*1t77=|`8=mckfnJ!xMrw+)6j)fY}U7L<#^UBR1yUp9Sa&%S9= z9vWFcEG2Y_SY;_X`>Hf(WP>64I>N zbR!EnZ{DGZ5@6uMiFBd0%Oh45u=@M_jE4_z6CFjXkm$<;e>3u7=`*}i@VL`^p)s)6 zgB{2nfp(~FMizgtVYeDTTZ!W9EFFNz!_#~a1+dU!T>tUo$8)h7_3&oQw??~1sGHV2 zFx_I8pcE>h6m*gYV^(L>w(d_g+jzD7>>(%^pp)wD;-1=e98ykG^t!Q~OOL=5!uhHf0Ge}=-{371<|KA zTyeb=hlQl3rwOHOPC&8hEz@lHRqU%M_J0iVIWQ&bYfUaJ1Y=UVYY{2 zkqY29=7>lf-p~Tt2-A$DYf(Gj1ygJShrIPr?)BO?nd7Ie%jod(-GT62nGN!n7guRA&#JM3MxR|{_L`G39 zuF84tZZrTlF@bVRdhXUkY$=a%mtcB5qv-6HJwNP>Xz5u52^P|m)vp1=Jv$otI|-U= z45tsmA4=?|t;EGBuU1rxeFF;o3JlY{)t{vJ{V%klTC}MX)^S;e;8<&md=g{yQo)%F zlkt*VN8M~vl@peFjYK;R@&6pESPY@9?K|e~L4<`vuVZ6fyB@2_;fX76Be07zmxE|!pyvq55 zNkYzA7A!q{A%D&tszUCGBF6E?nLpW z&7+@Cj-#u8vH>04l)BMmNKfW9lJTlz?I2pOsn@xBSQUc=#)VX2EHBiJ3A78eL#rhh z{E-F%j~R3I*bnOo;<|ZUlY+*_$G3=a_}_?fL=?|8!kdGFpplJq4Krj~qPc3d zI56?D2*5+*l>5E$v#rgx@!d#C9KQcrHJ}yqb$!;I3;sNwA*PzZjYDy}CS`RE^Vkf1 zopP$j87Wyc1>F|~OMi_S*;OK_AVKS%C(Fj6(o5uc+3<@H{qg^?-PfY_Y5o%rEW%RuJc;PHQ(}xlGcl0}J^jm2 zkSYu}zF9hSaZA>Hxmd&O@qgD~t}HI$>@|?ZP@2o&fZoUB=3ibHC0uZ1cO8m>>--qo zXlH@^lmf65@mBOpWOwet0)!pc-!*?S>&&=AbRo9O7jYry|3lv@G6 z_rm%+Lok{Pf4l*Ilrt(}IqfbSxiVQ_8V@KMGKvGPfr6RZ53;b|Lx(t5TBz-?NTWTd z7c^A$lfEhi+#u71pT@^qE%Nyf-1^lsHGgjIOVpUl1Z?OeLd#T2B~PYPqL`K&xS!wu zO{#%LS53Y9OsR3rx&1yAJ|%WG3;IZVKzZ9_jH>6eKQe$0i8@z? zTYasYHbX9iI*|&}wHI%Oi$? z2EehstBqpj91CdW!Er&dFk?&)Et{;3+P>t+Cm^*1;WVFH7-)$N4>^rHQT~=Y z!|w)TL;=A*P%r_V=3|q5;)KgSp}H5k3R}*XXmJuZ&&;*3Y$svS{&a3c&z41f5i3&n z;=#MbsBESO>LY8ruI@>8iZ#(@1pTpA;s81#819=HNiN|BuaVU3V`5?^ zEA4)Gs9m1>c_OTn3Qa*r0|In!*q`)1CwSzTZ|Yr(1CM(u0+(^su4<9HClxdgnle04 zZeCnw{)hJ6yq_t0<|VHBU6XQaKD?xXl2qDt@#`E%hS}9nel+w%B##T1Qb<#RW!rOM z3KA^Y)g(N7_H46?dT;sb)usrS?c&IUD+g#qqHpRRHAc{0z9&dP5P`yKH&zlClTqX= zKscx5y0L9$zZi~z_}$dRgZJ{iOuh?ck1NNm`m@z=_F@$QSbJC%wY#tfE`ah|@5{HV zQ*x@E6V3C93jp8_W=rtLa{o9ge%J)!%oOE(?f9;JMUorfQY>atW6D!O$0fHu)K4~X zNqDGvb73E%8sXgGLxI<~vn6UiqY$-s5K{cwWa7?N11{?RGPatz@Du3-z-<{L-8RUR zLL(8D*MeGoY`59O_u@%vAU3I&H$?4Z74#mRNA|<4ORU9{;@QmKT8Y0tziLGA!2qa` zi=<0C*!|WL&vtLkGki95MHJxnmr+(j=7#6}K2biE)xR?0f0@kUaV@bA6p3^VEvQYF zIp2yB0`JYy3)pFm*CWYJ#QY+XT?yB%c@7fW!`})3Xezd1+H9}Te-0YO{i7*a+?O6J z6aj|qyHNNOJdv4>wMYPSA%<)2Nx`WJE;}ha$?on|4SB58zTs*B_}-;d(WR41I+dpr zdbgt3h_@}Bh<{;@*LK^~ondwm;GGtCekm{X^5Eo13oNC$J4+5LxRxNHJ%ugb^5u5n zGFgR0_}Eur+F(YMoclTNp?5Yx+E~-E?!~{;sV7u3+l6M6*a40BC`c-Y8Ke0>ffPW+ z*qYPyj%Nr{U_TR_QAs8Lw8vH2M~H_Gx=t@dIyLh%e6Nwh56fe>bd|5w6QY~rR@158 z%>vMJkH`lU<}~lgB1HEHQ&re>cw`hMUB`&-Ios7kgy{7%e~PsfKzi25t3Rc3>t7!v zwlX!u8Ptdk98<=*;kiJNTtKRbaNUbN`0X>g*mzM#Wx+QPkcb7fvRj?1a~*E{Ul41# z9m5rY6J^EZb+oZ``@CKc^|gvUgOySxh5MqmH&`fiNN;mZHF3R16(I>rT>+&KV^93Y z2@5;L4l;a#AS5^gJNlQKiKT?e9p+L{|aTA36JZP@nD({M!|)HgjfHBB1rr7 zZs^WeUBKixqgDWe&}~PO?)XrNff9gqmL%4#~|kA8NA%$OCGLbpT#LJTxY zIbCcouan)8MFfabXNnzAxD0OlU{w$H*@jkT(L2J}W^ZMR#vQ_vF`=UV@OwOo3O z_MHEG6kh>BlyR9VNF3X+zxkQOZU7@o5j$zMdl501=Dp+%jQL+bnRvuN=oIaxdX^Wr z7^RiCXg-yX@8KX*V7vCa2@RvXd+f;^6#Wf58N4ZtHy&mu53s>Ca~g*HKdvV%SKrm1 zu%Ao(7=^;L@q*i?8@M1Ij5q8*jhYm_zQ2#)YKLdWESnWw*bkTw9i_J9w${|oSl@BN zeX(5VAyEazS6S-D+I~f^)Ny1US8ppMU12<@7q;$Z;R!pU{)0xffq5UiwwH6gkhoHv z#;6O%Ipq(dBxp`~Ku?6?BtQ9N+k=K0sEu6O((RjNv|z|JLrGr6#<(M0GM*D}D!q(2 zG}reg%n0e@$lR_aM-ajB%V}?w6^(ZyCVw%#_s>kL|1Ppgw>;;JxPA2Zj&<8ag+c4Y zXw)9+3jr!GPJo>>+^h`qnfBa%#{2G--ksO$afz|9>-~U1Tn#Lcumk$6*^w?x#UTAT`6_NL*rxh>4f=e0VbwNQNdmDXI%*>$VEOI7H>S{P$e} zmKPV0yXz(T)vA{1)>^kfhyoW0_e6_iuw=_0Z7ecQltI~rtO^0Nxvy*3bx}9gqB=3@ z2BhG7 zPVb;`M~q;{J?DS{5C=`lramxaeliwJfSI6HXqmKI{%8eNZkFViT})9xvL z9i8H=fSJ<3lL|Bu!J47nxN6va*jwe&Sdk}6P!CjYu?_x^&v@@hH6{8^-{f&w$lMl4 zI8i9f_~ZCE)J9X$*l$qcGdalx{m1d${_tD7k*qzp6A;1M%MO@tZ&3(*2mM6hU1xdBZd=Xjy4yBRYf{T{uLa!xL8D?9NA~ zFK%vc{H*05$;C)h;B8dy*Hi}=nzV7g7iITIn7VDyAJ1(o8^1mI4OxlPIO034^c5Gi z8@L=WU-|DKoI#}@>XK>=5~na7D;WDDCCQ}{&s)GwF38Wo-Q9pg#I3T99>&o@PGJhRT zlptU;L_9uscEZ`^BT&fglNsUsyU?j7pw~NwCSljr9e@uil!TWKhfcKNq(nG}T{>08 zvm)x3k+;Y9p2g|u=^j9c>r1Q5Rf+S58NrL$?f2fXPTg|fyQ~)Ja7{rF;sOqd@osgj?^OSqPu^GAN7&*^^0Wxe`Pal9$e~#Ft92f8bQzwWfL?T1ujb${Dsj@ z>{Z@Ep0R8JXI?pUX0^y8s!a|7(JuUvY2>{kd{o#Tz;^~CHraet|G_7xA3t@O0^$;o z!y732kOl9wD$)t3r)g9AdD&QU5RoQV=sGctXlFw|f$dXf3J=BqW=ZfNr>7CLk4ozVijeO!f5WOkNC2=pMC=E(5 z6iSe)|Ev)~W0YNb8#OmH|M2@C+48dsel&A#SBYnPDG9~<&ZOsym>2&J;~;@3E2HYl zc9U|m&j-@~!g+VIU9lQ?%6EGaEr4Nz{n>5;9=GZdez9>n@QpqoI-#~NlsK=S_=+1& zRiznE%Pp*56-lN$-2X;TM8M93A0=UD>-Pwfp5>BP#;3+7CML$|DfMr^CXHgijR{LG z?I!O;AkZ|L#MM`a;{Pnk7%Bca7k)7>i;eGE!}1?@QzV~P=*~TQzq@O!s3e;8tnCUc z5Csuz<@fOI@(%ZQL?!cau>YG^5i0`YU@`FpLcA$*qPZ1To&I_oo5GUlghzQ^Bu>6; zODE206Pg~+?YRneQbj0|&)8LU=YV7(^q^nn3v;K$0^ay5`w^jeh);h=!>x8M^VdMs z;T7!`Cv8fRT-c+760;t9ys^&{eCIyx1V2|35EsXj_+BqAhWt?ct)P=9NvXK?N(qQ( zQ!ji|pj(8a8UwdByQh!RE)xR`coyfyB_a3=*#5X9gEgqS>Z6F|5HE@pDoDkxW~&U< zLKOvM-=K%3_^{q9sI>*~i*w5;4qG7>eg~Rda?yP#OnJ@=IOU|w^_`6#Gh>!smk43q z2iF5UlbuvWZ_9C!+c^2IW#X$~?Qc`0CN6mkQr~2`Qx_N>q>C-&0HyZH^jfolIP0P1 zI?*aQ-*U_xLC1!7G%Eb$+{#wrE_7bSJJlLtsH91DP@2_erF|^GgY)cNuP5KcY7lsh zKQ|PnEej%bx>9QN8&2KZWFDJ*%L!7liz{;*E(tr9@O_6fnNo*7(hh3j$Gftgum6`? z6`;1}^<7Bpq_c-1&#Ben1JySy-h(gr%8BX#gPP$W&2zCyNu!Qd#}v`$Q!hm}mUn-X zg}u>_A3yG-t=8)W7H%m>zhuLmw|^HGxz4u{6RfCd-(`s&{LH-MQgHh;4W%aBd?(h$ zD0o#1HVQ<^XLEV$xADAGq{0#3^8Mv^SJB~o(AV|^=6#E@D~}Yqx_F3NEi{>4+ERNk zt%}?A5%ieJveR=O;e1vIMcU=B{x{}kSV}~+fD23lHwy@>v!3OWQ*UrLxLtLdKQ-wo zpBZUC!R>-LQxTY_Z8EH)qOvK4C`j53n_KkQ1@6HaR!Hm~UbIkMgnhZipW5~lpSVap z0DE$|B5 z>DUU9iqL_6^#u-HM%Gg=(*EUf)qM~-`bSri?~V!N-aeLSueR4=m;`2^h8=SujLKKk z?!Z{?JgdSw#Y}{Pyy^I8^gd@wiMApwb`SO7a2Y!}7XfF7)3Xt@yp0O>bXlmI6Ku7d zbKO&djAz`KJM-u<=`HbaI7GOu;5^Rla>o=N_4m52J4@GyH*Gcqggdd5-(>whJ3Xx= zS(u40l~1Wj;1{rgPzgiW%!nSaOVHVYIQ?ddBDujCaaPuM`uMW^+}B8w))JAcm%`0sy|*hNF8z}>NA4fE^9aU<71;oT+f3br z4cIXV9`0y|D6b1szL_|)iY5C(9${BsvNNp9iZ_O^)>H?i&ZTDTnSr>=aA=@6y|)%A zVBXnnQijva{cp*3ijzKfb1#LYakHX`ME4o=e|R(&2`_6x$;*oAOR$ln?i2 zP&fRpe*}B_!)ck;K^0tUwjAFMkHBGNqEUJ3@Oy8Z2A=-mLGRAG`9cC_&1=4$izYUh zzz7)=6sJcX@>}bQf_?A0y86K*KSmgb1_fDOuvVL~5ncH3_y^n4Ujkr-_YLDv1dbdc z7!QkszR<9+OB@T;453gJRn;9R4`sI6KKp|&e&K_hKvC8j=TB;`?tMQ@N}CpUMm*8i zPp(-onA=70n6{3#w>aairp*_jsD$MuerUz|iv9EO(w}TLP>gSVX6F*r~w=w z7d65#@EtgUGY zjzj6PPOhIghvNg64~2|XmUHRppI;Nq0Jz(O4-9`QR+tno+jX~{8mfnFog!Fw^@x(P z23p1 zH}IGng->$CKjq<)0b1tym)L4&t%o&0OSh`hQ1ebD;heMlxqp7loa6K=Y<8b0()zx` zgFYFL$uV*60XR**XMc;O1tKYTV20M`4&Kk4)*|;N#%#+ubj5wktLO?r%9?nELiQrh z)3KwLsBiJ(fKso{9W22-CRVdmWu*8ZbM@x5VR)>9=Rp1^J9ioi+B*7<=j`{tgC3p$ zEtWIG=Tc2%0v3l9b7y=_=^E&w<1CqhQmWln=7Aer;G)(oBTk9O6E;hv$jqT0FyVw_I&o%PX3oermHG|z$R9>I5^{9q^g?YU*6yQw*D`$cDJuq zJJ4WVW!9$8mDE3D(;1fr=M>BBp^zO}WxFvIFkta0;y!yYwBwBtnZ|a#wS(aMF>=F* zisltfd5(|*d6|U=@&=3vj`&}1kvQ_@`PO%zpSQ&($P4Q$N`0On<{+?M)2F{3p6x+n ziao9s?ZR6eSWe`KUw^OmiJ%CcD&!W{W68|*_33sa&-5UAR0K_r7mPZ{OJX=%J3~sA zi&S+^LH6y(*sYU2%Tj0E5Q^ZVT5NQwAI5Fw_l}+Hp`CrZhdt+kyIjNC1jO>=o8N0O z^m$zS|9W~lw%o=zF=8@rP*DKrSuMOd=@M+?)f7wVdw!S~l6BlF-NhnY9r6pbwSz^sYqrQPwsKnC^9&VAmA zzx*?EnLs4 zIQN!GXIj4Bc&wCC!+eCmDjs7Oxz+$*92!wK7d}0F!)lpO0t6#nvnC$a(mYhm8-^0x zr*y7<Lo{lhgAXYl=D;C?h|abHOAMTyQ|4of8JaDjs)xHD zB58f3uEi&TYy>~!>K-cBGZdJ;YvKZ##%}%(E-aM)DH1Ea?q}hW1zor>PcdjP{q&L& z%rTPJOCZI3b0BoCsdFzBw@*;{#Z_e%t_O$=5d)<^!=fuyO#wN*2Ub_m;Ri%vb^F?OcxWM4cCAnDHnZ5O(r z_4NDijF{v%A^?{i>fXNg=Mbxm%mO#WTD-YwBJBj?GAo~*OODFfpiax1$)6H@tEU|c za^}T>KslA)h4Ttz@G4}GU}J1#DeWX8f*8&3>5raUsgE(Pf?Q+k9L?_T1~ANU-LpAS~OCIVJFnW%`3xtVpp2U+u~U1dfUwFIW29g7q5Xym>t{ zibmy`?NygrQxX8o!y(yTT*3H^UOy(b|9Op%_AmB5I5W$I^@fJai4Z*ff8b|XNo!Ga zi=5y}ml(nGKGh0((ZSad9}&TS_N#nB=p^f3{6_-1{s$w@6ggih;?(|D)nz(IUo08uJ`GLqaPngXA+F6t@^i~Sq87QUQng#ICSoaYM`TP)r44O6XKthg;EKL_ zXM4woW>ve)ysIBvo{5K{4|^pf=mbcwFbX!>`<_2PGYV^2+ZfaC$@JzRgz3IT(JhOM zPfK3UL69S8EG@uTV)~DJg|7OHA0%dA?O=hlbD+w1&7MVx!*CmiEguU&?b$g%!qX>f z1wL>YEZ*J58(OE#e9Gx>%Hs znm+3~@J(E-pjoxrvR9K@4;U_%-dPEA8(kr6?wJBNGoub!rrwO$FvvE9Hz-_!O^Q}5 zWB9Oul2PhNegoQ&WWxq-A0l^6rfrk1ScsJw?f2AGGNmIrhi;%dzsngp!|EiQJCKcp zHqJO=B=KIDm|BYlx69oV5)OR#d3bD?!tJW)%o+HnVyyr07A{c?4Ad{*3>GG375`NI zY~GEnJWa8!xljlnPnsljJv3TKi1=u(dOppp6Vn_S{7Y7HV(oF{6;4`7S;l~NJ7#WW zWqVZBLh_qd9tV)LkiU#!Kh+b$mTc0*U4^c4**WlCiHkIs&e8`2fBIbSpx)-N{MhQJ ziHU7RX1B-8lg(CfVNTllY=fc17X=Rr%NsUY)a`EGc;riVVaYISvHr1tQeib|_U=e< z>8nSql1c+wSi9aj4hCL^`Emk{cWCRY);-Mj`*WR`M=dR>AIb%x1I_HjY?rtl&dg^Ze0j^V4#cgm|;3#>xK3rn# zLTvZQQNM?eQ$EFsn4JVVNilOH>Z8N>)s|3GIto9No`sc*WxRS+B!>(?xSU0XE#S0s zb8hq(BWmD{lZr_L|EL+JEz#Dbdg2LQxXwv7zTUlDTCX1KoT1(-1Oy&B3Rx#>W#~Ls z9}V0?(m?aG(H@tt#@g%#I_F5)o%ivqO%qR&0RY!cu*SwuKaSq&8 z5i^hdr%o&+QQ@dB`7ep+@_ShAp5dVBbPd3jtFo$J8+up3s_n1%Zzc8@$je^`4(`R3 z4ryVHAWdgGF^GCAI$m+c^(b17Gx19aY$rE~3Eo#DdZ=C<`A8gkz`eBlnRS zFytV~%d4q_o&kGES%Lv@fND!%+Kp&o&sMoE9P%~T!}X+fm)8@}aeGRbXU|=!olMrM z2k>Fy22TiEw3dbLPz$5%8cFV6sKmDD_rLPtapF1aAvuirTnHY|%_Y{3!0FqX&fIo0#ctHFjsqvMtetM|>ecpqj z8G-~lTK(j)X&HMnk9;or*|k~8KA@u+!nUSvvz}#|BqCII@NUQ0e{{?azE0RreelM3 z+W&eT*RZ0>3?`!@6eWT<4t5F zbg>J}D2mhA$lJ5b?yq_-Rvdhw;rKdE?6{j{absh1ykGcUQ#u>GrS>2ej*eRD(%_@P z+s8vp<>H8qurl!p5M8pUxxmjx8H3XVXy<=Vb9BeF)0c%vyRSmIz7n3)Pu~{^2YlTp z1M0=yYMFPLxd!(m)f_(X8;cGe^gXC@Hs*x#B1K0{W&wep5X-_UrgArt6-+; z3H9mew})M3@F^0YVL%J3UFCYmtTXOVl}n7n82eio?A{W(Z{_%SlnxawHe!`?1;P8n zV!pAdby?1SwLJ(PSHYL;jfK1R+I4x~xQbLj!i`^f=~ef&iX@PBxAs!Mwc|6eip_b$ z#yuUvbq9^1v;95pY4|JIZULEuS)Qsa3L9_dM-DlT5lIT5-hqa0D*I4=f*?yeovRxxkPrLSnyS3368tyvp2 zj+aT_hEuZw(|79YkMZQf}>TTT$7WRud2ZaUg;IIBG{?D8i#3ogwyZJKM z2nyvPzzqV!;45obuBR)EshXR-eZT2UDOpPl)T7et2-fPa^v zE?f!C1v(AtGV#KsdZnSb{+x->McC+_AYR}gnEo^kTP2riZoMIhnbHl2&;}%;R}BH< znzebD9CovGzpP{ZlP*LxY&BKc$5po+AMdNS$L)S0amDV{Q_YSV_&Eyo->bXzRg^wn zHNtBQ8lxz9PvmRIuouqtjtR;v+=ipWeu^Rp%2ctKWDhR4S-Y~ZTKsLQ%RB#H!%+Cl zMRS3^ocANO8)7{LwfG-a-Q2SL*r;XK*oXk!8D6~Ve<)eUW|eB^Smd8)JD;*~Q5Pkm zIgF20nB=D_(B*1Uv?_$z?5f=#%$>H3fiM;j22J|7D^LvaX>jG|FOS@N_p_chSYGjI zbwv(Q=3BH*n!#nm@IRtIib|r&?-OM?E`|TM3_GN0;4*3WjOWF7=s-^?`*z3O%U7 zzPveUN+Q3U?SnP~h<4|8(SMPN7sit^8;(FpSQtHfE&zxrNzRL*-5PpY`CK6tJen>cS#a!`d~H6tscca_Qjx%hQ3U&{E_0t=_{fJ--h?I&19{xS!hhJ}u9{mcoWf zVrphXJ=1JK5q{#L*(p{o&s=w=^ew|b)dUSMhx`w`NIMOQey7l$r!~kX*hD-4QQJyH zH}Cg3ChjNp7{yQQA&#xUPI;K*cE0y19GxC$ni{kBBI z=fG8!#}71`IMY{eUn42&pZL_RF7+P1W5b*0&r}n7qbOPGIvq61zSzV60ORuI)AbF{ zTAtM;{#lw*4>;!Qam46^^<;QGA&c3}g6$XKK&j$#!7OZ{-<)#yk8dXVJAN3Mp-GjVeog!)=4nDtnPQJ z%2ivW&d>~M+^HI$vL6h0E?k~{G$HGJQ7O5Vr&>F&3OZ;+bNrzC>|riaJ^U7JLBsKJ zv~v}FN(1ne+l^Mizpn(x7+LnD=3|w2E!y?bg{Z)>yHTw$f9`<_KmQ74Y(x5z@7l6~ ze?F1GGv*!(??=7eLO)LryefNZ$dE9lpLJ}D%niHZ_yX8H2pqWJz4&8XpRafHC4moA zRpd3Csu!d~DhVt<#U3@Z7_M8KI%GTSw*x`mLXaS53{Xy1VY+*nb+T{@^dF9ok#_mi z4=t9`vh8|3%aXUZdsDPH{eYdpFlk)=;^m^uhT~|`5P>)SB&hl_XMbuqC*47%NUR<6 z+z3G|45CAOjZe#L2*{HS+mvlT6B+}EHqVvdL+50zpD8JijmBp29pa$DYGJ+6M6SxU zSh#Lq+~eqq8=|=Rfb}7jkmQ-MO-ZLm3C%q82yr$-bZpz#)pO7frT5gmuAI+xCH}qm z7oHPn4lY-Y2Q{cK&2tZqOof@`UloTB^XwYoWgvud{$QxYrbXxe(BVeHF5)Jo@c`E< zCEaHW;m1Gv?gMs@sbNpiy<;EHfgewEyGp%%LeS;`nh~yJeTvy%mqMBRA~ zO+7zj=*NY2Ie+Ooz57v5>^RtrLoB6<7L?o0$KPlR!t!A?1Q@?^tdGuh<7_hC>Tpdb z30xR2msXkN0FH1hH`0y?9BSddtsvFCyi|RflCvEG_^t)fL){7c{H5x~7*P(g!udJ8 z#^{Kh%19m^UOJqvVK@qK`}hxdRQ#N*2DlrKE-cz=E=c!j`6}Ov4f&xLZp=oor~ld) zI~cDgsgzQxsGO72j=3QP#JQoGb}JhH9~aH&6@kpZ7YUA!VXIc*NZE&3Jsk-)idJ3@ z-Qz!J9iN`c`R7}oy6@^U$|*DnygyE!t{-r2?H#0bXF06bB_XJgQq9(U=b410xEz^k z8HsV?-y8&|(C&|OJaKW*-86$+9zlNI+iC{K{2!14J#^QIN(Y_eVZVx9^FCMp{WE+t zA>xI_Z={ciLXXb*Sa0nr7X%Uie14HMwQ8Oys4cS;>bL&{FeBtOv|LIsNQjoHo;dg4 zVQ0!~S!ha*<}($H_C?=a7+9aCJaaZ0;eB6^a<%GOJ{nMtu`EtiDZAO_i6(!Hs z38m4TrwIrVRj#LcuB_Hy{~g3{KZ_F5r;m~I8gslSHFu&>;DslXBoAAs@7RI^U;Nn z82Zq4g!tavN~yBe4efs}^d0uqJX(6-|8Ut(aB1dq<^MUzMTmn`ooeBIf9a8LVVaD@ zfk(SSg_tg#$FwnjU&=Dv4KyRa7A_S+!7-rV7@x)vg{M2K-V9xU)&BeM@atiCor*j^ zFV6yHwLW5Fi+#~hG#wA~8tUhCJqWiA|2rAp1B3ANWmlJ;k6)9VGYE0hKLaI5Q8l|{ z9)*Td(91DDYvyF8uIH*#fUr{xjy*Mex8rX>hwm$g^P%HFyZct4Vs}=mZvVY^w>ox{ zovOVj$HG?ydaT;fDo?G>-C`@P{ps@$AKVGI^>$AcImd?}I$?TJm6h$q8@M^k9b1V| zHqpC>%I+T8!!b|XJygp9hBxwZMmE*9clT0*kGgt)f{V;o!+|w+tPhReCq^vgcjfPz zeJNrFg0x`E0Kp6kl=9+Caq!%uoUAo8$l?W6PF1dnyjl|RJ-Byiq%_})yOAXCk?+TV zgM2yhE^_dG=Y3g>+O5p?+7JY1%LKEmdf3$Yx&kU8Tu|)Yy7QiI40NR__Pz6g-Wiwj zpuj2kk<%Fj8_%K1*Vk{U`-bG55nbS{k^{sCbDtMUU%p`NUY{v7x`|LbxaOZdi3@EUS&MfeR0wukRFXq_G3X*O=$7m^W>MccT78;4iFWoXO1-|9cIDLo~Iv zZaLpq98g&FX;V>+JL7*752Gr2>}{JE1!-5k>$xF!>A#wBQIg$+n_kIUxif0eQvb2* zefr`4jLJ~*q zDEKNje#*is55vr>!bK{wt!<>-zLU}6BDXuO=Sy`~D7;xW!y=l(puyKQYP`w|G#wt( z`zqoK^p!uoHI&Acn2Qltm?jyo_XYMFbtkQs^0=;K-lUooas-$6_lH$^{pVN*lzS=) z@5&F81bmd24HHS{coE{o)X>18(a>yzkvEQAdi+}OF)%UPvLQM)+)oW7;8S>WIZy_a zX0M2e5tU#6NnSjB=JVRxT3ZRLy!XFq1mIB9V)B8dtCLvr-WT!Hr@JptEA%cK5HOco zKQ6~J22jSUgJavailpd3k3`*ysCf9eyGSgwVnhsR2Isxzq^7pxzvK#v$NdP+W}9^d zG_|)WHfL5X{r1XK&!irho8mMcwr@JgzZ-d9G_DYiz2uyGF_jN+h~r{kGqB{xOSbPt~u@IH{I8-c(R97_(*0WXgaazXYZ;{kQaQ2zP*U* z3G|}lX<$c=Oj$f_$(z5j^7-c#jjcr>juIml-mD0ZSdytuaXn@52@qYMHYfa7hEq3n zHKx@rP#jtU*FPY@@X)1JkG|Dq#Z~bnlXOz7@IN~DVY9=jN(R1r8af#ULq)}$U9z}@P-wDoTqr)D12LstKew@=yK+D&rc=MBZ zj4-J*{Qy8~JgX?YkzVf~KF2xde?9sL;4^JD-ckHhp%;eTkkxHE}fm?q{R-X6UGNV{>!iK z8|5<|+JM>BYXf{sl+lpdV}EckKSpD#5Fwr!+hiQFqTo^F`P!IY#TR-+ZF1D|(a%ub zsA(Gq)UAqkjEYt4%afmbE6fZt5#fg2KT;Z6M$&%w&MH>92kXMtF+lK)HSay@ z^2Sfz>v;N!jh0^cr4C<56ovlk)hjI5xS)zJdTUVO+~FcF;MiS15&hyM{jxN*d-0`X zJyMN*9jVEj4AW4q9p&=3lL-zCGU6%1YvsZru%+u8?FiTIdg~)UdAmD$6Hw>0x_Mpm7Dr}}MTT8&_|6%IAj4~_PD;2UODB*-j)^X~-@amF?K0%#KUSjE+bYDzXxn?85hP^#1&Azw6JggS~uCa0tsEFvi_)*H}wkk&=ZPNJ}fp@VR*GgW)&>n zkR?%JlB}Kyw*u1uWiiGk&~mUni`5Z5mIauIW!gUk9@-Smn&CLcv7;qbf9#wyIK6Tw zxRK76B)y_(W7|Aafr?}^OlhQZK{zJMNqlTg!$QAPXeBX|v(U*?SzyT29=%O4m*DHR zM{cDK%dec(sTNt1jxUHMlXli+y~TF1Pg`r#NhSnrM5*Zj#L3D>5iW*gNk?)vVbSa+ zeVJ3+{Y-2=${=^bpC7${Th}ylz>?mSuOipT78usMGZOUM!hT_lbN0EMk5Y;yswd?| z#}X1C`<$TEg|iPVD^=!OcPEi5$MC?go9|pYj`9ur6f!1j&dsh_cM5Ghl?bl1#-B{Q z4rJNqRV7f@b>Ah8Eo8yRP`tnF2hlUbz=}Ot^O`LOu536Mp8*Cx)aHy|23dUgP|)P4 znIv3fhAB>VJjR0hb=}vbNMAg?NZ*#av+i^QZea`R@Rrbd*XcsJr6k_DxiYj8V|*;3 zVR(0oO5CjRaz*=L8ycvk%sA8c@K(PJE(PNtkp*?dENu~TD(&p}ha) z(reo2k*smnyxK^Vo><{+P1{pYN^E(U!H-PaMO>+hp~%RPvT}P} zdnCNvMkBG21?5}UasQFFaOUJFERJ862sBfDy#AdgOK&>fk8`8oIX*e>laCK%mHH{# zI|7P+jp3_nnDZqZifc>%y(O*h2$P7;0`k5;c)GRYs14NV4R-4E`4&Ol4ZAXa6~1Qb zbJHH6NDz3{>>wVjt_b&b@a?)Vfqgg`ohJSh5q32g<0TnL{f#$E3*!BG)%1lxJ^Nv1 zL9?ZJTafn{hnO6jQoGKvTnzHJy1^yUqc<5Bamul9H(p)Qn)>6y#3GBnxeX}`Nt;AB z4)VLC#%~8*lHLl*+(P+}xFlwCdOC5QAT7J_onnr>YG2ZJLu4MM1WxGj?aAo$^ZK3a zDF35cpyAtnmrkE{gK|>IeeKgFkf3BUl57}jtA2lR=;$@=VNwRZh;jL%cI%_9hQh^H z653{WCqL9(z+g4+>dCfH!JXA(Y%0S2q_l}-M@2(1dSZ0foIiJdmpqj+B7o1Y9>vI~if$zny>5J(z( zj{8yh+k*&r`*Ru5)%D#KH!O`=)FV$kq#_4Sj`wSsGfYlPR+^Z@h9+G&kYHKzDv#~@+(Vfd1E+Yp@;I5Ac#U=51 z&AwI8ZmGI{bqTT#Ac^$5<={}i-tW%gCG|g6y%Nuvjp0zc8^XhZ&?Ocj9Z?a7DwAz`PcK2yCb$@Oza^mTL3WXIqWrag#cXq&zfM~^%k zB5mDy#rz8O%#2twzogL;c?)LTQU<&mEqLx+6$bbtYRPvV2KNNljzyfYe7U_>%r8D} z`Th1Y$?x#vLP(FEwDyR`5-}ILhuW#tk7|lMp?F%H!9q~oK{}?|y!o;v9WqV_=jdB~ zj_Zx@tTNV5!0*el0DwWf!Nm#1qj~ub>GVd~-uEKF!X3VJXFx8e(y6FluL#)EHq7H4 zT(bE}ZN(Q2@hn)xXVIDYPNBQqwns?a!5}TkJ8}|=0yFX~Q4+&arseXcmh^t{5nEKF z5toeVIpO%=AH4z9@q$-WT=3{xPP&*SGyZt0bfnqz!fmbRKjI+r=BO8%Q;ZDmHF^n&I7nj^M4P#mx>1&> zV_E0B5H6NB`36n9Y)X?U-<|G7E$E*?ik7rJ$K5)KmvL03l!60>dz>W{$Hp?r8go~M zULTtc0a+vW=%PXs+Z>BGLb{V_5wG?NK&9)n&<#wR`e!frqqs}yp?}t{?D(H5Mncsh zUE~NU{k&fPNo3mUKAlIIbV!6GMo=TZ$c_fh9Az1ToZFH7!iIq_C+yEE1}(oEm?`L_ z{JV676|BzoQQ$7qUINM9tFr! zt}VyL^7*p;gh!$_@pP}mYM?%sDPWl#?^2cvE`N`gz#Fw3lR|~Y!TZ7F#}bb{RsX-( zAm#MJprfl}unz+ZzH@t*a)S|7G+t1&d`)CL7iF87EYEH|};{ z^Ub|N8&g7Ev$Vhfg3)O9-A1-iRIp$x@p1a6J_GLo!>i;O=OYOBPJRsErxF=;leAH; zk)$C8k+kahme>R(iei4s-fBJ-v%M88GwocqR zEkX=wXOSITC!oU!qNx`(y(0`70u;_KaRaA!SR1AXZ4$Rm1bXl?1eC?$P7dTL2+?QJ zJC5~BS-3;T=^o;y`!zr-f~6v5`48opwg+3^bcRbZ=nEVgI_OFME_D)=>k!&WZVhXH3=&t?d7G>her40VV4*mC+@y{n>`H|twUyKN*2kE2CaWivJ&-fk< zu8M|w;tFGcI6)^gS>6*F7FPZG zZ^%JiYai(g&C@i$2RW092zTTd{gUG*WjrnnAHs(3jQFU#U>qKN1g2+YQ|o6#2cr+o zvou6bCMoek$VyEp+@3g+D|4?Lh{<=r3I95$S_N`rc;r%KuED_0xfQ&R;$(IE?9xf2 zd788NGDD{{+g?yi*=(J(C+6t!5L9oCTN(MfTPj;*N+_)Zj?gD49VwgL*&EgUUVG0bJ+hIDK&r}1 zRN7nFE8A#bTQrIByCUF`Xhlyg$I(wmzMq}RW2MMveTc&)tmZ9UrW<=tv0Q=*wWFY4 z3UD7Ji)D6VHkHQH3l^Kg?%cu4VD;n=AYwv0w)9`g1cMQVUVL4u+~E=|mo+lA->ar{ z7dxfspziQ%5L3S!sXX`#4z0-edWI2BOouq_W%aOWSfsEKP5M9)btY26r~yL^_1z9v zPm*Cvwsb%qh17|_Fzp=s!BT)*6{=oKj$vOb8rEiU-oozdvU#w$ zr8t7fE8nSLdb>h;LX|0Y>^&TB-xNMGiKN7KpJXPf3{yMqFct=E=>7W>+6Z*Msiwid z3>*w3t@2m)#6fVGD1)3SI~asmcIF0q=|&SRu08m`bnNOj$iabCU8C9F z9XW92Kbe48|Ly(!it7&7Y!@j05N@mNk` zLYoN*vfTSOw%`b+LuT^`&z6o^p5Q`;qQxJ!Sp^?09slDt53W09Z-vGf{DH|4bxNLA z+G!NyBj9&*E+qaa^03>hHei+e@vKM@ak&5n-8}&Q(dZ=@bQ)WG-Q0&8&g;kZ`d~if z!)t4MQpBP7v8*M!H`5ZsmH*%3Gj@uzIdg+IyxO}>b+;f)x9~A_ z)ydjd2>47ZcpQ}ydD?)zepfhP5Qz{Bk9|326vpy$NYcT8A;mU+qse102 zjtjjCD^*4!vxftx%zBi8Bgm}(kakw8uXGjjn4)%G_9xU6A@27vot9r3ZPrN`?9&jQ zRai@qHAMFcb}CMg*~ebhVS9XTOYnTicxL3?tKMMIzb*3qzFx(-eQ2f1x6N&;pS?*1 zqjDR>`20nmqyMWrX$q7?$>dH%!;QuA&+1d3okR+*2zLb!NNdnH)jH($B%1C06VpW5 z$h%7)>Brh`kS^7AwMv!8q(e@TYU8^iyZ)7S>#USk~Z#h(x8F{Drr zOwk>Yue4P*DSVtfeJcR7xo~<^gU%^}H9d%El$$Q?v(Z|vAMbJCgguN63Ol!do*9YPF~ z>Kelp9-U}t=ZF#^-{EcOE9NnRWdn|ap~_-DZ}2XnFSxcV!Z*~ z;O&g$=4^=kFwzvqJX?|+p(*A>TKL+69T*#x#bO4i+q?fD49EHJgInE0j5@+FLj?)m zzT8D9(-W!N^kb0uj8x=i|HDonTw9oA17gRhaTjf;Y)cDJqSda>H>y1l~UM1fk)mzb`r5|9? zE6cvu;G06%uL?rDZpYsvi%wxh{I}zlK^!PhqdYTvX|a_-bAHjcmmuA9+wL)CwML`! z^SIy@+Y`uV7xm6O3Vhohv4%x32~x6zh_*@X@A|U3f1@*#V5MV?U-jOC5R6rLRhLa` zHZ!1I>v@4gzr*JBrB-M5U7Dp3Fs3M}Ldy=JVae*J$0L~uh?_SP{KJ%PNxO2D@m=ym zOA-R9V&a7VX$@=0D3Uag*REzGr|Tj(cm#mt)xk;bQJ>h4G| zwJm2a`J-{|2%go#8H3ctQ_|&2dqju-d?^tw;-kpwu3)B4yXI=_h%^nC%AuU?ip>Cq z>3uW)Nb_ZKvlw&-4=Y79nY7i-ZTCH>?>1P+qTs(N4%K~G)m~tDdv0T7TV(1hu8st5 zZ|QJx^ETI~;le3Xk=d0`h1lNMPx`W+_CSaVLX$3n8Il3^J_nI{mR#05#&&A1q`|b+QD&3kp4(T8m&Z+A& z&jN0al0W*kQ$FL6=1DNvGuDAaIq^HX!a{6JDtxBf>jb+GUSD(^xdw(E*bko~MG+xl zh%VHXvdqSae?A!tzf?51e9$j;KX(^P1C43bwd9@9EJPy2=cD<9x1JGmf1FSJd8oS( zr$-&lYxQ6D7-1*FrIpmwn3AT%IF?R+=FdWkjhRcl>Zt(&6)4Pp^N4pt-}3 zZ$oq^UjL+@a`7dt%{UR~ZH6|=m^E}$QX^(JoRx2qrnm8v6@$m9ypF~kGFkXVv?g8P zxT=~JFzTGZCz}l2=iAu&m^~}><6`?vyY7Rgwx7_l0dpW?@PQ%}C$ONML1_QF$ivQ* zx4DjG2hoqqJb%QWhs-Wc*4-eLMute>F@v!Z&!89Nmj=ps#E_ny`D~N{{cz?{dtZR6 z6&?CjHh1?VqCtEuCY_$zm6b^|_8>*}(s%n9;N`qO&Pr4lm;ZAykLJBW5~WdKlOBA z)1Yo$)y}cmOoUI_b%(i7CfGa~@nX%H9mhn_U@HV`QW!?EB|X7E8EXdl{tulec7RL7 z8b)}vyu9{%{h_Q33UJz^d!mPU{a#5+Qv-VxCuZ>MQ0Dff1>FtnAEOD1(p?cuKY@ur z5EIe}eZg3&Gqe+Mlxc-Gkw9vYR(T{W6V;_M zqz9hHkOh;Q8+B7o9(I`X;(?AowFQZ#%brlM1QbzK(sv)H78eze_~F9yr{M9dCG6tF zGQSEtxFTsdqTBg4CH6M-WX`24KV^z_Ajm0;g>dM#_WNpKcRd$s$UuGQe%kX8OPkRK zJdknbw2K(g-8ddH;lV&=1tXDQ5Rvt9a(k_7ii3e{MtKszInbxnxj(TBOJI<3RRuoq zFuW`A;qHpEo`6yZmQ4#G$f^b-uOFIITcum^6JU)=@H)rU;( zLmMTJg8c3wDH>>SiYS32*@=m3Dw#E)oxhZC`+VoiG}_f<=%JKz$^QH|#>`^)1>7^= z)iiizc4yZ%T_(w_C?##{P5|b!6c(vcKXP!`^BlB+xh6c_U>>xpBehF6!Ws1~cF>@@ z#lre?DM=x?3PyaapD{E?Pvukl{L|9pdH4z58z-*inX4cxOZwFSq0oMjC+dgb81GT8 zZ%bH6hJ}YS5;GF@(;R#M;tzh$;+Oxo8@@}X$SEr%xeZqSYqx)=104rX)AyXQQl#Uq5 zK{*J(&iPYW*8r(#0Cp8#V-ivf!n3L-nWz|1&+WyJasA!j+2ccy5|n5(A8?AJ_3kq) zDp{*qVqnYZ6=-^s&i|;?9e(K%uzpfi2lm2Qds{L3pGx5QA^vkb#OhsjFV4>nC?Enb zZqLqYnWad$K9HURn#WHRhI&RRy5T9{mPkt8Vqga#Q9f(pQmRnnxhpW zA5HA!r`xwPRHsaL=*_n5i|2h~lESmjO0K9%FXnwwuUnphtv(mVhjvo|23a9xvFzXvB=f3Hg5 zmCrG>zfse=CKu4FW^{RSn?oh% zVz-;@Xm^p{6%`hgg?rfkpOiXYx$-D>?ABH8bXre%j`noLWL_3KR}4~n6B_;V1}P2Z z69+=)qmiUlcFV@6o6PxS|G}PXicn)A^A1pYL}#~J6XNXuPL2Qn3J5+| z;c;fO@x|7#dJ(?g9-dx99=~FG9!T&bLH9@B4Il&v7N&VmJqkf$&J;b-St=?-8$J}! z`R2Y5Zlg->9bdj~?GhfmMCBN0v}}RH=J~d|XK>A3?%V&F|Qub*-}Nc zrfqq4J~G5AniMKpz%a(NF7CL~d_j{XpYy1Hq$#cQ>XThatr`dU;?Ix&U>5SgdWEG@ zZMAB2Ua6*!4N_)NcJO1HnLGbkBUBG`&{)2!~-se%WH~Cv??OX8x3`^HpUzT1PZ+ zaB9XAdhiO?%X1b z@1i|>;CSS_Qv?Irn+MDGUmxB};=lf7m!?Q>Xi;|ZDZNwA>i_E(>hWb|b9Sy8QM~zM zW~0A2ga;F&J(q)hodPy8D_ux?pek-_e`q-ia+39yk7}e$Rvuko5b@XJfOx8ywlv@4t2vDEQMn*

Zb}SFh5?>QaG-qYO`kM}CLNkCD_iBL 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 K9l#MLAz8BRh_ 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 h|8m*Tn!WYe*h^_s|)}D literal 0 HcmV?d00001 diff --git a/web/projects/shared/assets/img/storefront-outline.png b/web/projects/shared/assets/img/storefront-outline.png new file mode 100644 index 0000000000000000000000000000000000000000..aa7bd440493aac3fe53df067f6dc7382e6771c33 GIT binary patch literal 10912 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 Ae*gdg literal 0 HcmV?d00001 diff --git a/web/projects/shared/assets/taiga-ui/icons/tuiIconPaintOutline.svg b/web/projects/shared/assets/taiga-ui/icons/tuiIconPaintOutline.svg new file mode 100644 index 000000000..55450c05c --- /dev/null +++ b/web/projects/shared/assets/taiga-ui/icons/tuiIconPaintOutline.svg @@ -0,0 +1,10 @@ + + + diff --git a/web/projects/shared/src/components/loading/loading.component.scss b/web/projects/shared/src/components/loading/loading.component.scss new file mode 100644 index 000000000..9a7d10100 --- /dev/null +++ b/web/projects/shared/src/components/loading/loading.component.scss @@ -0,0 +1,20 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +:host { + @include shadow(3); + + display: flex; + align-items: center; + max-width: 80%; + margin: auto; + padding: 1.5rem; + background: var(--tui-elevation-01); + border-radius: var(--tui-radius-m); + + --tui-primary: var(--tui-warning-fill); +} + +tui-loader { + flex-shrink: 0; + min-width: 2rem; +} diff --git a/web/projects/shared/src/components/loading/loading.component.ts b/web/projects/shared/src/components/loading/loading.component.ts new file mode 100644 index 000000000..373f013a1 --- /dev/null +++ b/web/projects/shared/src/components/loading/loading.component.ts @@ -0,0 +1,17 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { + POLYMORPHEUS_CONTEXT, + PolymorpheusContent, +} from '@tinkoff/ng-polymorpheus' + +@Component({ + template: ` + + `, + styleUrls: ['./loading.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class LoadingComponent { + readonly content: PolymorpheusContent = + inject(POLYMORPHEUS_CONTEXT)['content'] +} diff --git a/web/projects/shared/src/components/loading/loading.module.ts b/web/projects/shared/src/components/loading/loading.module.ts new file mode 100644 index 000000000..4a3798041 --- /dev/null +++ b/web/projects/shared/src/components/loading/loading.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core' +import { TuiLoaderModule } from '@taiga-ui/core' +import { tuiAsDialog } from '@taiga-ui/cdk' +import { LoadingComponent } from './loading.component' +import { LoadingService } from './loading.service' + +@NgModule({ + imports: [TuiLoaderModule], + declarations: [LoadingComponent], + exports: [LoadingComponent], + providers: [tuiAsDialog(LoadingService)], +}) +export class LoadingModule {} diff --git a/web/projects/shared/src/components/loading/loading.service.ts b/web/projects/shared/src/components/loading/loading.service.ts new file mode 100644 index 000000000..96ab4301f --- /dev/null +++ b/web/projects/shared/src/components/loading/loading.service.ts @@ -0,0 +1,10 @@ +import { Injectable } from '@angular/core' +import { AbstractTuiDialogService } from '@taiga-ui/cdk' +import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' +import { LoadingComponent } from './loading.component' + +@Injectable({ providedIn: `root` }) +export class LoadingService extends AbstractTuiDialogService { + protected readonly component = new PolymorpheusComponent(LoadingComponent) + protected readonly defaultOptions = {} +} diff --git a/web/projects/shared/src/components/markdown/markdown.component.ts b/web/projects/shared/src/components/markdown/markdown.component.ts index d560f7537..e3f350e4a 100644 --- a/web/projects/shared/src/components/markdown/markdown.component.ts +++ b/web/projects/shared/src/components/markdown/markdown.component.ts @@ -3,7 +3,7 @@ import { ModalController } from '@ionic/angular' import { defer, isObservable, Observable, of } from 'rxjs' import { catchError, ignoreElements, share } from 'rxjs/operators' -import { getErrorMessage } from '../../services/error-toast.service' +import { getErrorMessage } from '../../services/error.service' @Component({ selector: 'markdown', diff --git a/web/projects/shared/src/public-api.ts b/web/projects/shared/src/public-api.ts index 7b1cd71b8..c83fef860 100644 --- a/web/projects/shared/src/public-api.ts +++ b/web/projects/shared/src/public-api.ts @@ -9,6 +9,9 @@ export * from './components/alert/alert.component' export * from './components/alert/alert.module' export * from './components/alert/alert-button.directive' export * from './components/alert/alert-input.directive' +export * from './components/loading/loading.component' +export * from './components/loading/loading.module' +export * from './components/loading/loading.service' export * from './components/markdown/markdown.component' export * from './components/markdown/markdown.component.module' export * from './components/text-spinner/text-spinner.component' @@ -42,7 +45,7 @@ export * from './pipes/unit-conversion/unit-conversion.pipe' export * from './services/download-html.service' export * from './services/emver.service' -export * from './services/error-toast.service' +export * from './services/error.service' export * from './services/http.service' export * from './themes/dark-theme/dark-theme.component' @@ -63,6 +66,7 @@ export * from './util/base-64' export * from './util/copy-to-clipboard' export * from './util/get-new-entries' export * from './util/get-pkg-id' +export * from './util/invert' export * from './util/misc.util' export * from './util/rpc.util' export * from './util/to-local-iso-string' diff --git a/web/projects/shared/src/services/error-toast.service.ts b/web/projects/shared/src/services/error-toast.service.ts deleted file mode 100644 index fe6607995..000000000 --- a/web/projects/shared/src/services/error-toast.service.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Injectable } from '@angular/core' -import { IonicSafeString, ToastController } from '@ionic/angular' -import { HttpError } from '../classes/http-error' - -@Injectable({ - providedIn: 'root', -}) -export class ErrorToastService { - private toast?: HTMLIonToastElement - - constructor(private readonly toastCtrl: ToastController) {} - - async present(e: HttpError | string, link?: string): Promise { - console.error(e) - - if (this.toast) return - - this.toast = await this.toastCtrl.create({ - header: 'Error', - message: getErrorMessage(e, link), - duration: 0, - position: 'top', - cssClass: 'error-toast', - buttons: [ - { - side: 'end', - icon: 'close', - handler: () => { - this.dismiss() - }, - }, - ], - }) - await this.toast.present() - } - - async dismiss(): Promise { - if (this.toast) { - await this.toast.dismiss() - this.toast = undefined - } - } -} - -export function getErrorMessage( - e: HttpError | string, - link?: string, -): string | IonicSafeString { - let message = '' - - if (typeof e === 'string') { - message = e - } else if (e.code === 0) { - message = - 'Request Error. Your browser blocked the request. This is usually caused by a corrupt browser cache or an overly aggressive ad blocker. Please clear your browser cache and/or adjust your ad blocker and try again' - link = 'https://docs.start9.com/0.3.5.x/support/common-issues#request-error' - } else if (!e.message) { - message = 'Unknown Error' - } else { - message = e.message - } - - if (link) { - return new IonicSafeString( - `${message}

Get Help`, - ) - } - - return message -} diff --git a/web/projects/shared/src/services/error.service.ts b/web/projects/shared/src/services/error.service.ts new file mode 100644 index 000000000..45891e0f4 --- /dev/null +++ b/web/projects/shared/src/services/error.service.ts @@ -0,0 +1,43 @@ +import { ErrorHandler, inject, Injectable } from '@angular/core' +import { TuiAlertService, TuiNotification } from '@taiga-ui/core' +import { HttpError } from '../classes/http-error' + +// TODO: Enable this as ErrorHandler +@Injectable({ + providedIn: 'root', +}) +export class ErrorService extends ErrorHandler { + private readonly alerts = inject(TuiAlertService) + + override handleError(error: HttpError | string, link?: string) { + console.error(error) + + this.alerts + .open(getErrorMessage(error, link), { + label: 'Error', + autoClose: false, + status: TuiNotification.Error, + }) + .subscribe() + } +} + +export function getErrorMessage(e: HttpError | string, link?: string): string { + let message = '' + + if (typeof e === 'string') { + message = e + } else if (e.code === 0) { + message = + 'Request Error. Your browser blocked the request. This is usually caused by a corrupt browser cache or an overly aggressive ad blocker. Please clear your browser cache and/or adjust your ad blocker and try again' + } else if (!e.message) { + message = 'Unknown Error' + link = 'https://docs.start9.com/latest/support/faq' + } else { + message = e.message + } + + return link + ? `${message}

Get Help` + : message +} diff --git a/web/projects/shared/src/util/invert.ts b/web/projects/shared/src/util/invert.ts new file mode 100644 index 000000000..6931b3660 --- /dev/null +++ b/web/projects/shared/src/util/invert.ts @@ -0,0 +1,12 @@ +export function invert< + T extends string | number | symbol, + D extends string | number | symbol, +>(obj: Record): Record { + const result = {} as Record + + for (const key in obj) { + result[obj[key]] = key + } + + return result +} diff --git a/web/projects/ui/src/app/app.module.ts b/web/projects/ui/src/app/app.module.ts index f13b4fbfa..048d81fe0 100644 --- a/web/projects/ui/src/app/app.module.ts +++ b/web/projects/ui/src/app/app.module.ts @@ -14,6 +14,7 @@ import { DarkThemeModule, EnterModule, LightThemeModule, + LoadingModule, MarkdownModule, ResponsiveColModule, SharedPipesModule, @@ -22,7 +23,6 @@ import { import { AppComponent } from './app.component' import { AppRoutingModule } from './app-routing.module' import { OSWelcomePageModule } from './modals/os-welcome/os-welcome.module' -import { GenericInputComponentModule } from './modals/generic-input/generic-input.component.module' import { MarketplaceModule } from './marketplace.module' import { PreloaderModule } from './app/preloader/preloader.module' import { FooterModule } from './app/footer/footer.module' @@ -49,7 +49,7 @@ import { environment } from '../environments/environment' EnterModule, OSWelcomePageModule, MarkdownModule, - GenericInputComponentModule, + LoadingModule, MonacoEditorModule, SharedPipesModule, MarketplaceModule, diff --git a/web/projects/ui/src/app/app.providers.ts b/web/projects/ui/src/app/app.providers.ts index e93c323d0..00ff64690 100644 --- a/web/projects/ui/src/app/app.providers.ts +++ b/web/projects/ui/src/app/app.providers.ts @@ -3,6 +3,7 @@ import { UntypedFormBuilder } from '@angular/forms' import { Router, RouteReuseStrategy } from '@angular/router' import { IonicRouteStrategy, IonNav } from '@ionic/angular' import { RELATIVE_URL, THEME, WorkspaceConfig } from '@start9labs/shared' +import { TUI_ICONS_PATH } from '@taiga-ui/core' import { PatchDB } from 'patch-db-client' import { PATCH_CACHE, @@ -53,6 +54,10 @@ export const APP_PROVIDERS: Provider[] = [ provide: THEME, useExisting: ThemeSwitcherService, }, + { + provide: TUI_ICONS_PATH, + useValue: (name: string) => `/assets/taiga-ui/icons/${name}.svg#${name}`, + }, ] export function appInitializer( diff --git a/web/projects/ui/src/app/app/preloader/preloader.component.html b/web/projects/ui/src/app/app/preloader/preloader.component.html index 94d6d7107..82923250e 100644 --- a/web/projects/ui/src/app/app/preloader/preloader.component.html +++ b/web/projects/ui/src/app/app/preloader/preloader.component.html @@ -8,8 +8,6 @@ - - @@ -19,14 +17,11 @@ - - - - - - - - - load bold font - - @@ -66,10 +53,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +

+
-

a

a

a

diff --git a/web/projects/ui/src/app/app/preloader/preloader.component.ts b/web/projects/ui/src/app/app/preloader/preloader.component.ts index 0b387ac45..f7184008e 100644 --- a/web/projects/ui/src/app/app/preloader/preloader.component.ts +++ b/web/projects/ui/src/app/app/preloader/preloader.component.ts @@ -1,4 +1,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' +import { + ActionSheetController, + AlertController, + ModalController, + ToastController, +} from '@ionic/angular' // TODO: Turn into DI token if this is needed someplace else too const ICONS = [ @@ -88,6 +94,26 @@ const ICONS = [ 'wifi', ] +const TAIGA = [ + 'tuiIconPaintOutline', + 'tuiIconTrash', + 'tuiIconTrashOutline', + 'tuiIconChevronDown', + 'tuiIconChevronDownOutline', + 'tuiIconRefreshCcw', + 'tuiIconRefreshCcwOutline', + 'tuiIconEye', + 'tuiIconEyeOutline', + 'tuiIconEyeOff', + 'tuiIconEyeOffOutline', + 'tuiIconPlus', + 'tuiIconMinus', + 'tuiIconCheck', + 'tuiIconClose', + 'tuiIconCalendarLarge', + 'tuiIconHelpCircle', +] + @Component({ selector: 'section[appPreloader]', templateUrl: 'preloader.component.html', @@ -95,4 +121,12 @@ const ICONS = [ }) export class PreloaderComponent { readonly icons = ICONS + readonly taiga = TAIGA + + constructor( + _modals: ModalController, + _alerts: AlertController, + _toasts: ToastController, + _actions: ActionSheetController, + ) {} } diff --git a/web/projects/ui/src/app/app/preloader/preloader.module.ts b/web/projects/ui/src/app/app/preloader/preloader.module.ts index b1496e638..380b70f3a 100644 --- a/web/projects/ui/src/app/app/preloader/preloader.module.ts +++ b/web/projects/ui/src/app/app/preloader/preloader.module.ts @@ -1,11 +1,65 @@ import { CommonModule } from '@angular/common' import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core' import { IonicModule } from '@ionic/angular' +import { + TuiErrorModule, + TuiExpandModule, + TuiLinkModule, + TuiScrollbarModule, + TuiSvgModule, + TuiTooltipModule, +} from '@taiga-ui/core' +import { + TuiButtonModule, + TuiCellModule, + TuiIconModule, +} from '@taiga-ui/experimental' +import { + TuiElasticContainerModule, + TuiInputDateModule, + TuiInputDateTimeModule, + TuiInputFilesModule, + TuiInputModule, + TuiInputNumberModule, + TuiInputTimeModule, + TuiMultiSelectModule, + TuiProgressModule, + TuiRadioListModule, + TuiSelectModule, + TuiTextAreaModule, + TuiToggleModule, +} from '@taiga-ui/kit' import { QrCodeModule } from 'ng-qrcode' import { PreloaderComponent } from './preloader.component' @NgModule({ - imports: [CommonModule, IonicModule, QrCodeModule], + imports: [ + CommonModule, + IonicModule, + QrCodeModule, + TuiTooltipModule, + TuiErrorModule, + TuiInputModule, + TuiSvgModule, + TuiIconModule, + TuiButtonModule, + TuiLinkModule, + TuiInputTimeModule, + TuiInputDateModule, + TuiInputDateTimeModule, + TuiInputFilesModule, + TuiMultiSelectModule, + TuiInputNumberModule, + TuiExpandModule, + TuiSelectModule, + TuiTextAreaModule, + TuiToggleModule, + TuiElasticContainerModule, + TuiCellModule, + TuiProgressModule, + TuiScrollbarModule, + TuiRadioListModule, + ], declarations: [PreloaderComponent], exports: [PreloaderComponent], schemas: [CUSTOM_ELEMENTS_SCHEMA], diff --git a/web/projects/ui/src/app/app/snek/snek.directive.ts b/web/projects/ui/src/app/app/snek/snek.directive.ts index db812ae4a..3e3ad5dc3 100644 --- a/web/projects/ui/src/app/app/snek/snek.directive.ts +++ b/web/projects/ui/src/app/app/snek/snek.directive.ts @@ -1,6 +1,6 @@ import { Directive, HostListener, Input } from '@angular/core' -import { LoadingController, ModalController } from '@ionic/angular' -import { ErrorToastService } from '@start9labs/shared' +import { ModalController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' import { SnakePage } from '../../modals/snake/snake.page' import { ApiService } from '../../services/api/embassy-api.service' @@ -13,8 +13,8 @@ export class SnekDirective { constructor( private readonly modalCtrl: ModalController, - private readonly loadingCtrl: LoadingController, - private readonly errToast: ErrorToastService, + private readonly loader: LoadingService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, ) {} @@ -30,12 +30,7 @@ export class SnekDirective { modal.onDidDismiss().then(async ({ data }) => { if (data?.highScore <= (this.appSnekHighScore || 0)) return - const loader = await this.loadingCtrl.create({ - message: 'Saving high score...', - backdropDismiss: true, - }) - - await loader.present() + const loader = this.loader.open('Saving high score...').subscribe() try { await this.embassyApi.setDbValue( @@ -43,9 +38,9 @@ export class SnekDirective { data.highScore, ) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - this.loadingCtrl.dismiss() + this.loader.subscribe() } }) diff --git a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.module.ts b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.module.ts index bf7f844e3..60c37d08d 100644 --- a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.module.ts +++ b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.module.ts @@ -10,7 +10,6 @@ import { UnitConversionPipesModule, TextSpinnerComponentModule, } from '@start9labs/shared' -import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.module' @NgModule({ declarations: [ @@ -23,7 +22,6 @@ import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form. IonicModule, UnitConversionPipesModule, TextSpinnerComponentModule, - GenericFormPageModule, ], exports: [ BackupDrivesComponent, diff --git a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts index fa73b8452..5613ae153 100644 --- a/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts +++ b/web/projects/ui/src/app/components/backup-drives/backup-drives.component.ts @@ -1,21 +1,18 @@ import { Component, EventEmitter, Input, Output } from '@angular/core' -import { BackupService } from './backup.service' +import { ActionSheetController, AlertController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { CB } from '@start9labs/start-sdk' import { CifsBackupTarget, DiskBackupTarget, RR, } from 'src/app/services/api/api.types' -import { - ActionSheetController, - AlertController, - LoadingController, - ModalController, -} from '@ionic/angular' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' -import { ConfigSpec } from 'src/app/pkg-config/config-types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ErrorToastService } from '@start9labs/shared' +import { FormDialogService } from 'src/app/services/form-dialog.service' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' +import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' +import { FormComponent } from '../form.component' +import { BackupService } from './backup.service' type BackupType = 'create' | 'restore' @@ -32,13 +29,13 @@ export class BackupDrivesComponent { loadingText = '' constructor( - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly actionCtrl: ActionSheetController, private readonly alertCtrl: AlertController, - private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly backupService: BackupService, + private readonly formDialog: FormDialogService, ) {} get loading() { @@ -87,23 +84,19 @@ export class BackupDrivesComponent { } async presentModalAddCifs(): Promise { - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: 'New Network Folder', - spec: CifsSpec, + this.formDialog.open(FormComponent, { + label: 'New Network Folder', + data: { + spec: await configBuilderToSpec(cifsSpec), buttons: [ { - text: 'Connect', - handler: (value: RR.AddBackupTargetReq) => { - return this.addCifs(value) - }, - isSubmit: true, + text: 'Execute', + handler: async (value: RR.AddBackupTargetReq) => + this.addCifs(value), }, ], }, }) - await modal.present() } async presentActionCifs( @@ -151,10 +144,9 @@ export class BackupDrivesComponent { } private async addCifs(value: RR.AddBackupTargetReq): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Testing connectivity to shared folder...', - }) - await loader.present() + const loader = this.loader + .open('Testing connectivity to shared folder...') + .subscribe() try { const res = await this.embassyApi.addBackupTarget(value) @@ -166,10 +158,10 @@ export class BackupDrivesComponent { }) return true } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) return false } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -180,62 +172,57 @@ export class BackupDrivesComponent { ): Promise { const { hostname, path, username } = entry - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: 'Update Shared Folder', - spec: CifsSpec, + this.formDialog.open(FormComponent, { + label: 'Update Network Folder', + data: { + spec: await configBuilderToSpec(cifsSpec), buttons: [ { - text: 'Save', - handler: (value: RR.AddBackupTargetReq) => { - return this.editCifs({ id, ...value }, index) - }, - isSubmit: true, + text: 'Execute', + handler: async (value: RR.AddBackupTargetReq) => + this.editCifs({ id, ...value }, index), }, ], - initialValue: { + value: { hostname, path, username, }, }, }) - await modal.present() } private async editCifs( value: RR.UpdateBackupTargetReq, index: number, - ): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Testing connectivity to shared folder...', - }) - await loader.present() + ): Promise { + const loader = this.loader + .open('Testing connectivity to shared folder...') + .subscribe() try { const res = await this.embassyApi.updateBackupTarget(value) this.backupService.cifs[index].entry = Object.values(res)[0] + + return true } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) + return false } finally { - loader.dismiss() + loader.unsubscribe() } } private async deleteCifs(id: string, index: number): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Removing...', - }) - await loader.present() + const loader = this.loader.open('Removing...').subscribe() try { await this.embassyApi.removeBackupTarget({ id }) this.backupService.cifs.splice(index, 1) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -274,40 +261,33 @@ export class BackupDrivesStatusComponent { @Input() hasValidBackup!: boolean } -const CifsSpec: ConfigSpec = { - hostname: { - type: 'string', - name: 'Hostname/IP', +const cifsSpec = CB.Config.of({ + hostname: CB.Value.text({ + name: 'Hostname', description: - 'The hostname or IP address of the target device on your Local Area Network.', - placeholder: `e.g. 'MyComputer.local' OR '192.168.1.4'`, - nullable: false, - masked: false, - copyable: false, - }, - path: { - type: 'string', + 'The hostname of your target device on the Local Area Network.', + warning: null, + placeholder: `e.g. 'My Computer' OR 'my-computer.local'`, + required: { default: null }, + patterns: [], + }), + path: CB.Value.text({ name: 'Path', description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`, placeholder: 'e.g. my-shared-folder or /Desktop/my-folder', - nullable: false, - masked: false, - copyable: false, - }, - username: { - type: 'string', + required: { default: null }, + }), + username: CB.Value.text({ name: 'Username', description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`, - nullable: false, - masked: false, - copyable: false, - }, - password: { - type: 'string', + required: { default: null }, + placeholder: 'My Network Folder', + }), + password: CB.Value.text({ name: 'Password', description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`, - nullable: true, + required: false, masked: true, - copyable: false, - }, -} + placeholder: 'My Network Folder', + }), +}) diff --git a/web/projects/ui/src/app/components/form-object/form-label.component.html b/web/projects/ui/src/app/components/form-object/form-label.component.html deleted file mode 100644 index 69c29f99d..000000000 --- a/web/projects/ui/src/app/components/form-object/form-label.component.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - -{{ data.name }} - - (New) - (New Options) - (Edited) - - * diff --git a/web/projects/ui/src/app/components/form-object/form-object.component.html b/web/projects/ui/src/app/components/form-object/form-object.component.html deleted file mode 100644 index 8bbfc5c78..000000000 --- a/web/projects/ui/src/app/components/form-object/form-object.component.html +++ /dev/null @@ -1,372 +0,0 @@ - -
-
- - - -

- -

- - - - - - - - - - {{ spec.units }} - - -

- - {{ errors | getError: $any(spec)['pattern-description'] }} - -

-
- - - - - - - - {{ spec.name }} - - (New) - - - (Edited) - - - - - - - - - - {{ spec['value-names'][option] }} - - - - - - - - - - - - -
- -
-
-
- - - - - - - - - - - Add - - -

- - {{ errors | getError }} - -

- -
-
- - - - - - - - - - - -
- - - Delete - -
-
-
- -
- - - - - - -

- - {{ errors | getError: $any(spec)['pattern-description'] }} - -

-
-
-
-
-
- - - - -

- -

- - - -

{{ formArr.value | toEnumListDisplay: $any(spec.spec) }}

-
- - - -
-

- - {{ errors | getError }} - -

-
-
-
-
-
diff --git a/web/projects/ui/src/app/components/form-object/form-object.component.module.ts b/web/projects/ui/src/app/components/form-object/form-object.component.module.ts deleted file mode 100644 index 06af388c0..000000000 --- a/web/projects/ui/src/app/components/form-object/form-object.component.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { - FormObjectComponent, - FormUnionComponent, - FormLabelComponent, -} from './form-object.component' -import { - GetErrorPipe, - ToWarningTextPipe, - ToElementIdPipe, - GetControlPipe, - ToEnumListDisplayPipe, - ToRangePipe, -} from './form-object.pipes' -import { IonicModule } from '@ionic/angular' -import { FormsModule, ReactiveFormsModule } from '@angular/forms' -import { SharedPipesModule } from '@start9labs/shared' -import { TuiElasticContainerModule } from '@taiga-ui/kit' -import { EnumListPageModule } from 'src/app/modals/enum-list/enum-list.module' -import { TuiExpandModule } from '@taiga-ui/core' - -@NgModule({ - declarations: [ - FormObjectComponent, - FormUnionComponent, - FormLabelComponent, - ToWarningTextPipe, - GetErrorPipe, - ToEnumListDisplayPipe, - ToElementIdPipe, - GetControlPipe, - ToRangePipe, - ], - imports: [ - CommonModule, - IonicModule, - FormsModule, - ReactiveFormsModule, - SharedPipesModule, - EnumListPageModule, - TuiElasticContainerModule, - TuiExpandModule, - ], - exports: [FormObjectComponent, FormLabelComponent], -}) -export class FormObjectComponentModule {} diff --git a/web/projects/ui/src/app/components/form-object/form-object.component.scss b/web/projects/ui/src/app/components/form-object/form-object.component.scss deleted file mode 100644 index a98a0ff72..000000000 --- a/web/projects/ui/src/app/components/form-object/form-object.component.scss +++ /dev/null @@ -1,42 +0,0 @@ -.slot-start { - display: inline-block; - vertical-align: middle; - --padding-start: 0; - --padding-end: 7px; -} - -.error-border { - border-color: var(--ion-color-danger-shade); - --border-color: var(--ion-color-danger-shade); -} - -.redacted { - font-family: 'Redacted' -} - -ion-input { - font-family: 'Courier New'; - font-weight: bold; - --placeholder-font-weight: 400; -} - -ion-item-divider { - text-transform: unset; - --padding-top: 18px; - --padding-start: 0; - border-bottom: 1px solid var(--ion-item-border-color, var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13)))) -} - -.nested-wrapper { - padding: 0 0 16px 24px; -} - -.error-message { - margin-top: 2px; - font-size: small; - color: var(--ion-color-danger); -} - -.indent { - margin-left: 24px; -} \ No newline at end of file diff --git a/web/projects/ui/src/app/components/form-object/form-object.component.ts b/web/projects/ui/src/app/components/form-object/form-object.component.ts deleted file mode 100644 index fcc6a0c61..000000000 --- a/web/projects/ui/src/app/components/form-object/form-object.component.ts +++ /dev/null @@ -1,425 +0,0 @@ -import { - Component, - Input, - Output, - EventEmitter, - ChangeDetectionStrategy, - Inject, - inject, - SimpleChanges, -} from '@angular/core' -import { FormArray, UntypedFormArray, UntypedFormGroup } from '@angular/forms' -import { AlertButton, AlertController, ModalController } from '@ionic/angular' -import { - ConfigSpec, - ListValueSpecOf, - ValueSpec, - ValueSpecBoolean, - ValueSpecEnum, - ValueSpecList, - ValueSpecListOf, - ValueSpecUnion, -} from 'src/app/pkg-config/config-types' -import { FormService } from 'src/app/services/form.service' -import { EnumListPage } from 'src/app/modals/enum-list/enum-list.page' -import { THEME, pauseFor } from '@start9labs/shared' -import { v4 } from 'uuid' -import { DOCUMENT } from '@angular/common' - -const Mustache = require('mustache') - -interface Config { - [key: string]: any -} -@Component({ - selector: 'form-object', - templateUrl: './form-object.component.html', - styleUrls: ['./form-object.component.scss'], -}) -export class FormObjectComponent { - @Input() objectSpec!: ConfigSpec - @Input() formGroup!: UntypedFormGroup - @Input() current?: Config - @Input() original?: Config - @Output() onInputChange = new EventEmitter() - @Output() hasNewOptions = new EventEmitter() - warningAck: { [key: string]: boolean } = {} - unmasked: { [key: string]: boolean } = {} - objectDisplay: { - [key: string]: { expanded: boolean; hasNewOptions: boolean } - } = {} - objectListDisplay: { - [key: string]: { expanded: boolean; displayAs: string }[] - } = {} - objectId = v4() - - readonly theme$ = inject(THEME) - - constructor( - private readonly alertCtrl: AlertController, - private readonly modalCtrl: ModalController, - private readonly formService: FormService, - @Inject(DOCUMENT) private readonly document: Document, - ) {} - - ngOnInit() { - this.setDisplays() - - // setTimeout hack to avoid ExpressionChangedAfterItHasBeenCheckedError - setTimeout(() => { - if ( - this.original && - Object.keys(this.current || {}).some( - key => this.original![key] === undefined, - ) - ) - this.hasNewOptions.emit() - }) - } - - ngOnChanges(changes: SimpleChanges) { - const specChanges = changes['objectSpec'] - - if (!specChanges) return - - if ( - !specChanges.firstChange && - Object.keys({ - ...specChanges.previousValue, - ...specChanges.currentValue, - }).length !== Object.keys(specChanges.previousValue).length - ) { - this.setDisplays() - } - } - - private setDisplays() { - Object.keys(this.objectSpec).forEach(key => { - const spec = this.objectSpec[key] - - if (spec.type === 'list' && ['object', 'union'].includes(spec.subtype)) { - this.objectListDisplay[key] = [] - this.formGroup.get(key)?.value.forEach((obj: any, index: number) => { - const displayAs = (spec.spec as ListValueSpecOf<'object'>)[ - 'display-as' - ] - this.objectListDisplay[key][index] = { - expanded: false, - displayAs: displayAs - ? (Mustache as any).render(displayAs, obj) - : '', - } - }) - } else if (spec.type === 'object') { - this.objectDisplay[key] = { - expanded: false, - hasNewOptions: false, - } - } - }) - } - - addListItemWrapper( - key: string, - spec: T extends ValueSpecUnion ? never : T, - ) { - this.presentAlertChangeWarning(key, spec, () => this.addListItem(key)) - } - - toggleExpandObject(key: string) { - this.objectDisplay[key].expanded = !this.objectDisplay[key].expanded - } - - toggleExpandListObject(key: string, i: number) { - this.objectListDisplay[key][i].expanded = - !this.objectListDisplay[key][i].expanded - } - - updateLabel(key: string, i: number, displayAs: string) { - this.objectListDisplay[key][i].displayAs = displayAs - ? Mustache.render(displayAs, this.formGroup.get(key)?.value[i]) - : '' - } - - handleInputChange() { - this.onInputChange.emit() - } - - setHasNew(key: string) { - this.hasNewOptions.emit() - setTimeout(() => { - this.objectDisplay[key].hasNewOptions = true - }, 100) - } - - handleBooleanChange(key: string, spec: ValueSpecBoolean) { - if (spec.warning) { - const current = this.formGroup.get(key)?.value - const cancelFn = () => this.formGroup.get(key)?.setValue(!current) - this.presentAlertChangeWarning(key, spec, undefined, cancelFn) - } - } - - async presentModalEnumList( - key: string, - spec: ValueSpecListOf<'enum'>, - current: string[], - ) { - const modal = await this.modalCtrl.create({ - componentProps: { - key, - spec, - current, - }, - component: EnumListPage, - }) - - modal.onWillDismiss().then(({ data }) => { - if (!data) return - this.updateEnumList(key, current, data) - }) - - await modal.present() - } - - async presentAlertChangeWarning( - key: string, - spec: T extends ValueSpecUnion ? never : T, - okFn?: Function, - cancelFn?: Function, - ) { - if (!spec.warning || this.warningAck[key]) return okFn ? okFn() : null - this.warningAck[key] = true - - const buttons: AlertButton[] = [ - { - text: 'Ok', - handler: () => { - if (okFn) okFn() - }, - cssClass: 'enter-click', - }, - ] - - if (okFn || cancelFn) { - buttons.unshift({ - text: 'Cancel', - handler: () => { - if (cancelFn) cancelFn() - }, - }) - } - - const alert = await this.alertCtrl.create({ - header: 'Warning', - subHeader: `Editing ${spec.name} has consequences:`, - message: spec.warning, - buttons, - }) - await alert.present() - } - - async presentAlertDelete(key: string, index: number) { - const alert = await this.alertCtrl.create({ - header: 'Confirm', - message: 'Are you sure you want to delete this entry?', - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Delete', - handler: () => { - this.deleteListItem(key, index) - }, - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } - - async presentAlertBoolEnumDescription( - event: Event, - spec: ValueSpecBoolean | ValueSpecEnum, - ) { - event.stopPropagation() - const { name, description } = spec - - const alert = await this.alertCtrl.create({ - header: name, - message: description, - buttons: [ - { - text: 'OK', - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } - - private addListItem(key: string): void { - const arr = this.formGroup.get(key) as UntypedFormArray - const listSpec = this.objectSpec[key] as ValueSpecList - const newItem = this.formService.getListItem(listSpec, undefined)! - - const index = arr.length - arr.insert(index, newItem) - - if (['object', 'union'].includes(listSpec.subtype)) { - const displayAs = (listSpec.spec as ListValueSpecOf<'object'>)[ - 'display-as' - ] - this.objectListDisplay[key].push({ - expanded: false, - displayAs: displayAs ? Mustache.render(displayAs, newItem.value) : '', - }) - } - - setTimeout(() => { - const element = this.document.getElementById( - getElementId(this.objectId, key, index), - ) - element?.parentElement?.scrollIntoView({ behavior: 'smooth' }) - - if (['object', 'union'].includes(listSpec.subtype)) { - pauseFor(250).then(() => this.toggleExpandListObject(key, index)) - } - }, 100) - - arr.markAsDirty() - } - - private deleteListItem(key: string, index: number, markDirty = true): void { - // if (this.objectListDisplay[key]) - // this.objectListDisplay[key][index].height = '0px' - const arr = this.formGroup.get(key) as UntypedFormArray - if (markDirty) arr.markAsDirty() - pauseFor(250).then(() => { - if (this.objectListDisplay[key]) - this.objectListDisplay[key].splice(index, 1) - arr.removeAt(index) - }) - } - - private updateEnumList(key: string, current: string[], updated: string[]) { - const arr = this.formGroup.get(key) as FormArray - - for (let i = current.length - 1; i >= 0; i--) { - if (!updated.includes(current[i])) { - arr.removeAt(i) - } - } - - const listSpec = this.objectSpec[key] as ValueSpecList - - updated.forEach(val => { - if (!current.includes(val)) { - const newItem = this.formService.getListItem(listSpec, val)! - arr.insert(arr.length, newItem) - } - }) - - arr.markAsDirty() - } - - asIsOrder() { - return 0 - } -} - -@Component({ - selector: 'form-union', - templateUrl: './form-union.component.html', - styleUrls: ['./form-object.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FormUnionComponent { - @Input() formGroup!: UntypedFormGroup - @Input() spec!: ValueSpecUnion - @Input() current?: Config - @Input() original?: Config - - get unionValue() { - return this.formGroup.get(this.spec.tag.id)?.value - } - - get isNew() { - return !this.original - } - - get hasNewOptions() { - const tagId = this.spec.tag.id - return ( - this.original?.[tagId] === this.current?.[tagId] && - !!Object.keys(this.current || {}).find( - key => this.original![key] === undefined, - ) - ) - } - - objectId = v4() - - constructor(private readonly formService: FormService) {} - - updateUnion(e: any): void { - const tagId = this.spec.tag.id - - Object.keys(this.formGroup.controls).forEach(control => { - if (control === tagId) return - this.formGroup.removeControl(control) - }) - - const unionGroup = this.formService.getUnionObject( - this.spec as ValueSpecUnion, - e.detail.value, - ) - - Object.keys(unionGroup.controls).forEach(control => { - if (control === tagId) return - this.formGroup.addControl(control, unionGroup.controls[control]) - }) - } -} - -@Component({ - selector: 'form-label', - templateUrl: './form-label.component.html', - styleUrls: ['./form-object.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FormLabelComponent { - @Input() data!: { - name: string - new: boolean - edited: boolean - description?: string - required?: boolean - newOptions?: boolean - } - - constructor(private readonly alertCtrl: AlertController) {} - - async presentAlertDescription(event: Event) { - event.stopPropagation() - const { name, description } = this.data - - const alert = await this.alertCtrl.create({ - header: name, - message: description, - buttons: [ - { - text: 'OK', - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } -} - -export function getElementId(objectId: string, key: string, index = 0): string { - return `${key}-${index}-${objectId}` -} diff --git a/web/projects/ui/src/app/components/form-object/form-object.pipes.ts b/web/projects/ui/src/app/components/form-object/form-object.pipes.ts deleted file mode 100644 index 1dc5a18f2..000000000 --- a/web/projects/ui/src/app/components/form-object/form-object.pipes.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core' -import { - AbstractControl, - FormGroup, - UntypedFormArray, - ValidationErrors, -} from '@angular/forms' -import { IonicSafeString } from '@ionic/angular' -import { ListValueSpecOf } from 'src/app/pkg-config/config-types' -import { Range } from 'src/app/pkg-config/config-utilities' -import { getElementId } from './form-object.component' - -@Pipe({ - name: 'getError', -}) -export class GetErrorPipe implements PipeTransform { - transform(errors: ValidationErrors, patternDesc?: string): string { - if (errors['required']) { - return 'Required' - } else if (errors['pattern']) { - return patternDesc || 'Invalid pattern' - } else if (errors['notNumber']) { - return 'Must be a number' - } else if (errors['numberNotInteger']) { - return 'Must be an integer' - } else if (errors['numberNotInRange']) { - return errors['numberNotInRange'].value - } else if (errors['listNotUnique']) { - return errors['listNotUnique'].value - } else if (errors['listNotInRange']) { - return errors['listNotInRange'].value - } else if (errors['listItemIssue']) { - return errors['listItemIssue'].value - } else { - return 'Unknown error' - } - } -} - -@Pipe({ - name: 'toEnumListDisplay', -}) -export class ToEnumListDisplayPipe implements PipeTransform { - transform(arr: string[], spec: ListValueSpecOf<'enum'>): string { - return arr.map((v: string) => spec['value-names'][v]).join(', ') - } -} - -@Pipe({ - name: 'toWarningText', -}) -export class ToWarningTextPipe implements PipeTransform { - transform(text?: string): IonicSafeString | string { - return text - ? new IonicSafeString(`${text}`) - : '' - } -} - -@Pipe({ - name: 'toRange', -}) -export class ToRangePipe implements PipeTransform { - transform(range: string): Range { - return Range.from(range) - } -} - -@Pipe({ - name: 'toElementId', -}) -export class ToElementIdPipe implements PipeTransform { - transform(objectId: string, key: string, index = 0): string { - return getElementId(objectId, key, index) - } -} - -@Pipe({ - name: 'getControl', -}) -export class GetControlPipe implements PipeTransform { - transform( - formGroup: FormGroup, - key: string, - index?: number, - ): AbstractControl { - const abstractControl = formGroup.get(key)! - if (index !== undefined) - return (abstractControl as UntypedFormArray).at(index) - return abstractControl - } -} diff --git a/web/projects/ui/src/app/components/form-object/form-union.component.html b/web/projects/ui/src/app/components/form-object/form-union.component.html deleted file mode 100644 index ed9fc31be..000000000 --- a/web/projects/ui/src/app/components/form-object/form-union.component.html +++ /dev/null @@ -1,42 +0,0 @@ -
- - - - - - - {{ spec.tag['variant-names'][option.key] }} - - - - - - - -
diff --git a/web/projects/ui/src/app/components/form.component.ts b/web/projects/ui/src/app/components/form.component.ts new file mode 100644 index 000000000..830e8de1e --- /dev/null +++ b/web/projects/ui/src/app/components/form.component.ts @@ -0,0 +1,167 @@ +import { CommonModule } from '@angular/common' +import { + ChangeDetectionStrategy, + Component, + inject, + Input, + OnInit, +} from '@angular/core' +import { FormGroup, ReactiveFormsModule } from '@angular/forms' +import { RouterModule } from '@angular/router' +import { CT } from '@start9labs/start-sdk' + +import { + tuiMarkControlAsTouchedAndValidate, + TuiValueChangesModule, +} from '@taiga-ui/cdk' +import { TuiDialogContext, TuiModeModule } from '@taiga-ui/core' +import { TuiButtonModule } from '@taiga-ui/experimental' +import { TuiDialogFormService } from '@taiga-ui/kit' +import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus' +import { compare, Operation } from 'fast-json-patch' +import { FormModule } from 'src/app/components/form/form.module' +import { InvalidService } from 'src/app/components/form/invalid.service' +import { FormService } from 'src/app/services/form.service' + +export interface ActionButton { + text: string + handler?: (value: T) => Promise | void + link?: string +} + +export interface FormContext { + spec: CT.InputSpec + buttons: ActionButton[] + value?: T + patch?: Operation[] +} + +@Component({ + standalone: true, + selector: 'app-form', + template: ` +
+ + +
+ `, + styles: [ + ` + footer { + position: sticky; + bottom: 0; + z-index: 10; + display: flex; + justify-content: flex-end; + padding: 1rem 0; + margin: 1rem 0 -1rem; + gap: 1rem; + background: var(--tui-elevation-01); + border-top: 1px solid var(--tui-base-02); + } + `, + ], + imports: [ + CommonModule, + ReactiveFormsModule, + RouterModule, + TuiValueChangesModule, + TuiButtonModule, + TuiModeModule, + FormModule, + ], + providers: [InvalidService], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FormComponent> implements OnInit { + private readonly dialogFormService = inject(TuiDialogFormService) + private readonly formService = inject(FormService) + private readonly invalidService = inject(InvalidService) + private readonly context = inject>>( + POLYMORPHEUS_CONTEXT, + { optional: true }, + ) + + @Input() spec = this.context?.data.spec || {} + @Input() buttons = this.context?.data.buttons || [] + @Input() patch = this.context?.data.patch || [] + @Input() value?: T = this.context?.data.value + + form = new FormGroup({}) + + ngOnInit() { + this.dialogFormService.markAsPristine() + this.form = this.formService.createForm(this.spec, this.value) + this.process(this.patch) + } + + onReset() { + const { value } = this.form + + this.form = this.formService.createForm(this.spec) + this.process(compare(this.form.value, value)) + tuiMarkControlAsTouchedAndValidate(this.form) + this.markAsDirty() + } + + async onClick(handler: Required>['handler']) { + tuiMarkControlAsTouchedAndValidate(this.form) + this.invalidService.scrollIntoView() + + if (this.form.valid && (await handler(this.form.value as T))) { + this.close() + } + } + + markAsDirty() { + this.dialogFormService.markAsDirty() + } + + close() { + this.context?.$implicit.complete() + } + + private process(patch: Operation[]) { + patch.forEach(({ op, path }) => { + const control = this.form.get(path.substring(1).split('/')) + + if (!control || !control.parent) return + + if (op !== 'remove') { + control.markAsDirty() + control.markAsTouched() + } + + control.parent.markAsDirty() + control.parent.markAsTouched() + }) + } +} diff --git a/web/projects/ui/src/app/components/form/control.directive.ts b/web/projects/ui/src/app/components/form/control.directive.ts new file mode 100644 index 000000000..327eb8255 --- /dev/null +++ b/web/projects/ui/src/app/components/form/control.directive.ts @@ -0,0 +1,30 @@ +import { Directive, ElementRef, inject, OnDestroy, OnInit } from '@angular/core' +import { ControlContainer, NgControl } from '@angular/forms' +import { InvalidService } from './invalid.service' + +@Directive({ + selector: 'form-control, form-array, form-object', +}) +export class ControlDirective implements OnInit, OnDestroy { + private readonly invalidService = inject(InvalidService, { optional: true }) + private readonly element: ElementRef = inject(ElementRef) + private readonly control = + inject(NgControl, { optional: true }) || + inject(ControlContainer, { optional: true }) + + get invalid(): boolean { + return !!this.control?.invalid + } + + scrollIntoView() { + this.element.nativeElement.scrollIntoView({ behavior: 'smooth' }) + } + + ngOnInit() { + this.invalidService?.add(this) + } + + ngOnDestroy() { + this.invalidService?.remove(this) + } +} diff --git a/web/projects/ui/src/app/components/form/control.ts b/web/projects/ui/src/app/components/form/control.ts new file mode 100644 index 000000000..476826194 --- /dev/null +++ b/web/projects/ui/src/app/components/form/control.ts @@ -0,0 +1,35 @@ +import { inject } from '@angular/core' +import { FormControlComponent } from './form-control/form-control.component' +import { CT } from '@start9labs/start-sdk' + +export abstract class Control { + private readonly control: FormControlComponent = + inject(FormControlComponent) + + get invalid(): boolean { + return this.control.touched && this.control.invalid + } + + get spec(): Spec { + return this.control.spec + } + + // TODO: Properly handle already set immutable value + get readOnly(): boolean { + return ( + !!this.value && !!this.control.control?.pristine && this.control.immutable + ) + } + + get value(): Value | null { + return this.control.value + } + + set value(value: Value | null) { + this.control.onInput(value) + } + + onFocus(focused: boolean) { + this.control.onFocus(focused) + } +} diff --git a/web/projects/ui/src/app/components/form/form-array/form-array.component.html b/web/projects/ui/src/app/components/form/form-array/form-array.component.html new file mode 100644 index 000000000..76c67f837 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-array/form-array.component.html @@ -0,0 +1,58 @@ +
+ {{ spec.name }} + + +
+ + + + + {{ item.value | mustache: $any(spec.spec).displayAs }} + + + + + + + + + diff --git a/web/projects/ui/src/app/components/form/form-array/form-array.component.scss b/web/projects/ui/src/app/components/form/form-array/form-array.component.scss new file mode 100644 index 000000000..9b6415ff7 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-array/form-array.component.scss @@ -0,0 +1,50 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +:host { + display: block; + margin: 2rem 0; +} + +.label { + display: flex; + font-size: 1.25rem; + font-weight: bold; +} + +.add { + font-size: 1rem; + padding: 0 1rem; + margin-left: auto; +} + +.object { + display: block; + position: relative; + + &_open::after, + &:last-child::after { + opacity: 0; + } + + &:after { + @include transition(opacity); + + content: ''; + position: absolute; + bottom: -0.5rem; + height: 1px; + left: 3rem; + right: 1rem; + background: var(--tui-clear); + } +} + +.remove { + margin-left: auto; + pointer-events: auto; +} + +.control { + display: block; + margin: 0.5rem 0; +} diff --git a/web/projects/ui/src/app/components/form/form-array/form-array.component.ts b/web/projects/ui/src/app/components/form/form-array/form-array.component.ts new file mode 100644 index 000000000..11495d510 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-array/form-array.component.ts @@ -0,0 +1,91 @@ +import { Component, HostBinding, inject, Input } from '@angular/core' +import { AbstractControl, FormArrayName } from '@angular/forms' +import { TUI_PARENT_ANIMATION, TuiDestroyService } from '@taiga-ui/cdk' +import { + TUI_ANIMATION_OPTIONS, + TuiDialogService, + tuiFadeIn, + tuiHeightCollapse, +} from '@taiga-ui/core' +import { TUI_PROMPT } from '@taiga-ui/kit' +import { CT } from '@start9labs/start-sdk' +import { filter, takeUntil } from 'rxjs' +import { FormService } from 'src/app/services/form.service' +import { ERRORS } from '../form-group/form-group.component' + +@Component({ + selector: 'form-array', + templateUrl: './form-array.component.html', + styleUrls: ['./form-array.component.scss'], + animations: [tuiFadeIn, tuiHeightCollapse, TUI_PARENT_ANIMATION], + providers: [TuiDestroyService], +}) +export class FormArrayComponent { + @Input() + spec!: CT.ValueSpecList + + @HostBinding('@tuiParentAnimation') + readonly animation = { value: '', ...inject(TUI_ANIMATION_OPTIONS) } + readonly order = ERRORS + readonly array = inject(FormArrayName) + readonly open = new Map() + + private warned = false + private readonly formService = inject(FormService) + private readonly dialogs = inject(TuiDialogService) + private readonly destroy$ = inject(TuiDestroyService) + + get canAdd(): boolean { + return ( + !this.spec.disabled && + (!this.spec.maxLength || + this.spec.maxLength >= this.array.control.controls.length) + ) + } + + add() { + if (!this.warned && this.spec.warning) { + this.dialogs + .open(TUI_PROMPT, { + label: 'Warning', + size: 's', + data: { content: this.spec.warning, yes: 'Ok', no: 'Cancel' }, + }) + .pipe(filter(Boolean), takeUntil(this.destroy$)) + .subscribe(() => { + this.addItem() + }) + } else { + this.addItem() + } + + this.warned = true + } + + removeAt(index: number) { + this.dialogs + .open(TUI_PROMPT, { + label: 'Confirm', + size: 's', + data: { + content: 'Are you sure you want to delete this entry?', + yes: 'Delete', + no: 'Cancel', + }, + }) + .pipe(filter(Boolean), takeUntil(this.destroy$)) + .subscribe(() => { + this.removeItem(index) + }) + } + + private removeItem(index: number) { + this.open.delete(this.array.control.at(index)) + this.array.control.removeAt(index) + } + + private addItem() { + this.array.control.insert(0, this.formService.getListItem(this.spec)) + this.open.set(this.array.control.at(0), true) + } +} diff --git a/web/projects/ui/src/app/components/form/form-color/form-color.component.html b/web/projects/ui/src/app/components/form/form-color/form-color.component.html new file mode 100644 index 000000000..19cf4051d --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-color/form-color.component.html @@ -0,0 +1,31 @@ + + {{ spec.name }} + * + + +
+ + +
+
diff --git a/web/projects/ui/src/app/components/form/form-color/form-color.component.scss b/web/projects/ui/src/app/components/form/form-color/form-color.component.scss new file mode 100644 index 000000000..49496946e --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-color/form-color.component.scss @@ -0,0 +1,33 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +.wrapper { + position: relative; + width: 1.5rem; + height: 1.5rem; + pointer-events: auto; + + &::after { + content: ''; + position: absolute; + height: 0.3rem; + width: 1.4rem; + bottom: 0.125rem; + background: currentColor; + border-radius: 0.125rem; + pointer-events: none; + } +} + +.color { + @include fullsize(); + opacity: 0; +} + +.icon { + @include fullsize(); + pointer-events: none; + + input:hover + & { + opacity: 1; + } +} diff --git a/web/projects/ui/src/app/components/form/form-color/form-color.component.ts b/web/projects/ui/src/app/components/form/form-color/form-color.component.ts new file mode 100644 index 000000000..32a7c1c04 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-color/form-color.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' +import { MaskitoOptions } from '@maskito/core' + +@Component({ + selector: 'form-color', + templateUrl: './form-color.component.html', + styleUrls: ['./form-color.component.scss'], +}) +export class FormColorComponent extends Control { + readonly mask: MaskitoOptions = { + mask: ['#', ...Array(6).fill(/[0-9a-f]/i)], + } +} diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.component.html b/web/projects/ui/src/app/components/form/form-control/form-control.component.html new file mode 100644 index 000000000..614aae05c --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-control/form-control.component.html @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + {{ spec.warning }} +

This value cannot be changed once set!

+
+ + +
+
diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.component.scss b/web/projects/ui/src/app/components/form/form-control/form-control.component.scss new file mode 100644 index 000000000..844651118 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-control/form-control.component.scss @@ -0,0 +1,11 @@ +:host { + display: block; +} + +.buttons { + margin-top: 0.5rem; + + :first-child { + margin-right: 0.5rem; + } +} diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.component.ts b/web/projects/ui/src/app/components/form/form-control/form-control.component.ts new file mode 100644 index 000000000..ec49bd084 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-control/form-control.component.ts @@ -0,0 +1,71 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + Input, + TemplateRef, + ViewChild, +} from '@angular/core' +import { AbstractTuiNullableControl } from '@taiga-ui/cdk' +import { + TuiAlertService, + TuiDialogContext, + TuiNotification, +} from '@taiga-ui/core' +import { filter, takeUntil } from 'rxjs' +import { CT } from '@start9labs/start-sdk' +import { ERRORS } from '../form-group/form-group.component' +import { FORM_CONTROL_PROVIDERS } from './form-control.providers' + +@Component({ + selector: 'form-control', + templateUrl: './form-control.component.html', + styleUrls: ['./form-control.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: FORM_CONTROL_PROVIDERS, +}) +export class FormControlComponent< + T extends CT.ValueSpec, + V, +> extends AbstractTuiNullableControl { + @Input() + spec!: T + + @ViewChild('warning') + warning?: TemplateRef> + + warned = false + focused = false + readonly order = ERRORS + private readonly alerts = inject(TuiAlertService) + + get immutable(): boolean { + return 'immutable' in this.spec && this.spec.immutable + } + + onFocus(focused: boolean) { + this.focused = focused + this.updateFocused(focused) + } + + onInput(value: V | null) { + const previous = this.value + + if (!this.warned && this.warning) { + this.alerts + .open(this.warning, { + label: 'Warning', + status: TuiNotification.Warning, + hasCloseButton: false, + autoClose: false, + }) + .pipe(filter(Boolean), takeUntil(this.destroy$)) + .subscribe(() => { + this.value = previous + }) + } + + this.warned = true + this.value = value === '' ? null : value + } +} diff --git a/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts b/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts new file mode 100644 index 000000000..f065f86cb --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-control/form-control.providers.ts @@ -0,0 +1,25 @@ +import { forwardRef, Provider } from '@angular/core' +import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit' +import { CT } from '@start9labs/start-sdk' +import { FormControlComponent } from './form-control.component' + +interface ValidatorsPatternError { + actualValue: string + requiredPattern: string | RegExp +} + +export const FORM_CONTROL_PROVIDERS: Provider[] = [ + { + provide: TUI_VALIDATION_ERRORS, + deps: [forwardRef(() => FormControlComponent)], + useFactory: (control: FormControlComponent) => ({ + required: 'Required', + pattern: ({ requiredPattern }: ValidatorsPatternError) => + ('patterns' in control.spec && + control.spec.patterns.find( + ({ regex }) => String(regex) === String(requiredPattern), + )?.description) || + 'Invalid format', + }), + }, +] diff --git a/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.html b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.html new file mode 100644 index 000000000..05ffa69f3 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.html @@ -0,0 +1,43 @@ + + + {{ spec.name }} + * + + + {{ spec.name }} + * + + + {{ spec.name }} + * + + diff --git a/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts new file mode 100644 index 000000000..e09b22d24 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core' +import { + TUI_FIRST_DAY, + TUI_LAST_DAY, + TuiDay, + tuiPure, + TuiTime, +} from '@taiga-ui/cdk' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-datetime', + templateUrl: './form-datetime.component.html', +}) +export class FormDatetimeComponent extends Control< + CT.ValueSpecDatetime, + string +> { + readonly min = TUI_FIRST_DAY + readonly max = TUI_LAST_DAY + + @tuiPure + getTime(value: string | null) { + return value ? TuiTime.fromString(value) : null + } + + getLimit(limit: string): [TuiDay, TuiTime] { + return [ + TuiDay.jsonParse(limit.slice(0, 10)), + limit.length === 10 + ? new TuiTime(0, 0) + : TuiTime.fromString(limit.slice(-5)), + ] + } +} diff --git a/web/projects/ui/src/app/components/form/form-file/form-file.component.html b/web/projects/ui/src/app/components/form/form-file/form-file.component.html new file mode 100644 index 000000000..9e9c16613 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-file/form-file.component.html @@ -0,0 +1,31 @@ + + + +
+
+ {{ spec.name }} + * + +
+ + + Click or drop file here + +
+
Drop file here
+
+
diff --git a/web/projects/ui/src/app/components/form/form-file/form-file.component.scss b/web/projects/ui/src/app/components/form/form-file/form-file.component.scss new file mode 100644 index 000000000..2e314972d --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-file/form-file.component.scss @@ -0,0 +1,46 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +.template { + @include transition(opacity); + + width: 100%; + display: flex; + align-items: center; + padding: 0 0.5rem; + font: var(--tui-font-text-m); + font-weight: bold; + + &_hidden { + opacity: 0; + } +} + +.drop { + @include fullsize(); + @include transition(opacity); + display: flex; + align-items: center; + justify-content: space-around; + + &_hidden { + opacity: 0; + } +} + +.label { + display: flex; + align-items: center; + max-width: 50%; +} + +small { + max-width: 50%; + font-weight: normal; + color: var(--tui-text-02); + margin-left: auto; +} + +tui-tag { + z-index: 1; + margin: -0.25rem -0.25rem -0.25rem auto; +} diff --git a/web/projects/ui/src/app/components/form/form-file/form-file.component.ts b/web/projects/ui/src/app/components/form/form-file/form-file.component.ts new file mode 100644 index 000000000..52d340fa6 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-file/form-file.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core' +import { TuiFileLike } from '@taiga-ui/kit' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-file', + templateUrl: './form-file.component.html', + styleUrls: ['./form-file.component.scss'], +}) +export class FormFileComponent extends Control {} diff --git a/web/projects/ui/src/app/components/form/form-group/form-group.component.html b/web/projects/ui/src/app/components/form/form-group/form-group.component.html new file mode 100644 index 000000000..d3c769b98 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-group/form-group.component.html @@ -0,0 +1,30 @@ + + + + + + diff --git a/web/projects/ui/src/app/components/form/form-group/form-group.component.scss b/web/projects/ui/src/app/components/form/form-group/form-group.component.scss new file mode 100644 index 000000000..ce5665fc0 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-group/form-group.component.scss @@ -0,0 +1,35 @@ +form-group .g-form-control:not(:first-child) { + margin-top: 1rem; +} + +form-group .g-form-group { + position: relative; + padding-left: var(--tui-height-m); + + &::before, + &::after { + content: ''; + position: absolute; + background: var(--tui-clear); + } + + &::before { + top: 0; + left: calc(1rem - 1px); + bottom: 0.5rem; + width: 2px; + } + + &::after { + left: 0.75rem; + bottom: 0; + width: 0.5rem; + height: 0.5rem; + border-radius: 100%; + } +} + +form-group tui-tooltip { + z-index: 1; + margin-left: 0.25rem; +} diff --git a/web/projects/ui/src/app/components/form/form-group/form-group.component.ts b/web/projects/ui/src/app/components/form/form-group/form-group.component.ts new file mode 100644 index 000000000..d9d28c8df --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-group/form-group.component.ts @@ -0,0 +1,35 @@ +import { + ChangeDetectionStrategy, + Component, + Input, + ViewEncapsulation, +} from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { FORM_GROUP_PROVIDERS } from './form-group.providers' + +export const ERRORS = [ + 'required', + 'pattern', + 'notNumber', + 'numberNotInteger', + 'numberNotInRange', + 'listNotUnique', + 'listNotInRange', + 'listItemIssue', +] + +@Component({ + selector: 'form-group', + templateUrl: './form-group.component.html', + styleUrls: ['./form-group.component.scss'], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + viewProviders: [FORM_GROUP_PROVIDERS], +}) +export class FormGroupComponent { + @Input() spec: CT.InputSpec = {} + + asIsOrder() { + return 0 + } +} diff --git a/web/projects/ui/src/app/components/form/form-group/form-group.providers.ts b/web/projects/ui/src/app/components/form/form-group/form-group.providers.ts new file mode 100644 index 000000000..5c9039f40 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-group/form-group.providers.ts @@ -0,0 +1,34 @@ +import { Provider, SkipSelf } from '@angular/core' +import { + TUI_ARROW_MODE, + tuiInputDateOptionsProvider, + tuiInputTimeOptionsProvider, +} from '@taiga-ui/kit' +import { TUI_DEFAULT_ERROR_MESSAGE } from '@taiga-ui/core' +import { ControlContainer } from '@angular/forms' +import { identity, of } from 'rxjs' + +export const FORM_GROUP_PROVIDERS: Provider[] = [ + { + provide: TUI_DEFAULT_ERROR_MESSAGE, + useValue: of('Unknown error'), + }, + { + provide: ControlContainer, + deps: [[new SkipSelf(), ControlContainer]], + useFactory: identity, + }, + { + provide: TUI_ARROW_MODE, + useValue: { + interactive: null, + disabled: null, + }, + }, + tuiInputDateOptionsProvider({ + nativePicker: true, + }), + tuiInputTimeOptionsProvider({ + nativePicker: true, + }), +] diff --git a/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.html b/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.html new file mode 100644 index 000000000..0e2a47cc2 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.html @@ -0,0 +1,18 @@ + + {{ spec.name }} + + diff --git a/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts b/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts new file mode 100644 index 000000000..7134eb1f6 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts @@ -0,0 +1,49 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' +import { tuiPure } from '@taiga-ui/cdk' +import { invert } from '@start9labs/shared' + +@Component({ + selector: 'form-multiselect', + templateUrl: './form-multiselect.component.html', +}) +export class FormMultiselectComponent extends Control< + CT.ValueSpecMultiselect, + readonly string[] +> { + private readonly inverted = invert(this.spec.values) + + private readonly isDisabled = (item: string) => + Array.isArray(this.spec.disabled) && + this.spec.disabled.includes(this.inverted[item]) + + private readonly isExceedingLimit = (item: string) => + !!this.spec.maxLength && + this.selected.length >= this.spec.maxLength && + !this.selected.includes(item) + + readonly disabledItemHandler = (item: string): boolean => + this.isDisabled(item) || this.isExceedingLimit(item) + + readonly items = Object.values(this.spec.values) + + get disabled(): boolean { + return typeof this.spec.disabled === 'string' + } + + get selected(): string[] { + return this.memoize(this.value) + } + + set selected(value: string[]) { + this.value = Object.entries(this.spec.values) + .filter(([_, v]) => value.includes(v)) + .map(([k]) => k) + } + + @tuiPure + private memoize(value: null | readonly string[]): string[] { + return value?.map(key => this.spec.values[key]) || [] + } +} diff --git a/web/projects/ui/src/app/components/form/form-number/form-number.component.html b/web/projects/ui/src/app/components/form/form-number/form-number.component.html new file mode 100644 index 000000000..c205b2bb8 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-number/form-number.component.html @@ -0,0 +1,18 @@ + + {{ spec.name }} + * + + diff --git a/web/projects/ui/src/app/components/form/form-number/form-number.component.ts b/web/projects/ui/src/app/components/form/form-number/form-number.component.ts new file mode 100644 index 000000000..a930b1614 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-number/form-number.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-number', + templateUrl: './form-number.component.html', +}) +export class FormNumberComponent extends Control { + protected readonly Infinity = Infinity +} diff --git a/web/projects/ui/src/app/components/form/form-object/form-object.component.html b/web/projects/ui/src/app/components/form/form-object/form-object.component.html new file mode 100644 index 000000000..589019c15 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-object/form-object.component.html @@ -0,0 +1,25 @@ +

+ + + {{ spec.name }} + +

+ + +
+ +
+
diff --git a/web/projects/ui/src/app/components/form/form-object/form-object.component.scss b/web/projects/ui/src/app/components/form/form-object/form-object.component.scss new file mode 100644 index 000000000..c167c89fa --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-object/form-object.component.scss @@ -0,0 +1,41 @@ +@import '@taiga-ui/core/styles/taiga-ui-local'; + +:host { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.title { + position: relative; + height: var(--tui-height-l); + display: flex; + align-items: center; + cursor: pointer; + font: var(--tui-font-text-l); + font-weight: bold; + margin: 0 0 -0.75rem; +} + +.button { + @include transition(transform); + + margin-right: 1rem; + + &_open { + transform: rotate(180deg); + } +} + +.expand { + align-self: stretch; +} + +.g-form-group { + padding-top: 0.75rem; + + &_invalid::before, + &_invalid::after { + background: var(--tui-error-bg); + } +} diff --git a/web/projects/ui/src/app/components/form/form-object/form-object.component.ts b/web/projects/ui/src/app/components/form/form-object/form-object.component.ts new file mode 100644 index 000000000..b1aa507cf --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-object/form-object.component.ts @@ -0,0 +1,38 @@ +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + inject, + Input, + Output, +} from '@angular/core' +import { ControlContainer } from '@angular/forms' +import { CT } from '@start9labs/start-sdk' + +@Component({ + selector: 'form-object', + templateUrl: './form-object.component.html', + styleUrls: ['./form-object.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FormObjectComponent { + @Input() + spec!: CT.ValueSpecObject + + @Input() + open = false + + @Output() + readonly openChange = new EventEmitter() + + private readonly container = inject(ControlContainer) + + get invalid() { + return !this.container.valid && this.container.touched + } + + toggle() { + this.open = !this.open + this.openChange.emit(this.open) + } +} diff --git a/web/projects/ui/src/app/components/form/form-select/form-select.component.html b/web/projects/ui/src/app/components/form/form-select/form-select.component.html new file mode 100644 index 000000000..c10e62018 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-select/form-select.component.html @@ -0,0 +1,17 @@ + + {{ spec.name }} + * + + diff --git a/web/projects/ui/src/app/components/form/form-select/form-select.component.ts b/web/projects/ui/src/app/components/form/form-select/form-select.component.ts new file mode 100644 index 000000000..ccbbccae8 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-select/form-select.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { invert } from '@start9labs/shared' +import { Control } from '../control' + +@Component({ + selector: 'form-select', + templateUrl: './form-select.component.html', +}) +export class FormSelectComponent extends Control { + private readonly inverted = invert(this.spec.values) + + readonly items = Object.values(this.spec.values) + + readonly disabledItemHandler = (item: string) => + Array.isArray(this.spec.disabled) && + this.spec.disabled.includes(this.inverted[item]) + + get disabled(): boolean { + return typeof this.spec.disabled === 'string' + } + + get selected(): string | null { + return (this.value && this.spec.values[this.value]) || null + } + + set selected(value: string | null) { + this.value = (value && this.inverted[value]) || null + } +} diff --git a/web/projects/ui/src/app/components/form/form-text/form-text.component.html b/web/projects/ui/src/app/components/form/form-text/form-text.component.html new file mode 100644 index 000000000..0db900238 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-text/form-text.component.html @@ -0,0 +1,44 @@ + + {{ spec.name }} + * + + + + + + diff --git a/web/projects/ui/src/app/components/form/form-text/form-text.component.scss b/web/projects/ui/src/app/components/form/form-text/form-text.component.scss new file mode 100644 index 000000000..05e47885b --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-text/form-text.component.scss @@ -0,0 +1,8 @@ +.button { + pointer-events: auto; + margin-left: 0.25rem; +} + +.masked { + -webkit-text-security: disc; +} diff --git a/web/projects/ui/src/app/components/form/form-text/form-text.component.ts b/web/projects/ui/src/app/components/form/form-text/form-text.component.ts new file mode 100644 index 000000000..8a5ac86f0 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-text/form-text.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core' +import { CT, utils } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-text', + templateUrl: './form-text.component.html', + styleUrls: ['./form-text.component.scss'], +}) +export class FormTextComponent extends Control { + masked = true + + generate() { + this.value = utils.getDefaultString(this.spec.generate || '') + } +} diff --git a/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.html b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.html new file mode 100644 index 000000000..7a2bf5149 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.html @@ -0,0 +1,15 @@ + + {{ spec.name }} + * + + diff --git a/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts new file mode 100644 index 000000000..b32685c21 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-textarea', + templateUrl: './form-textarea.component.html', +}) +export class FormTextareaComponent extends Control< + CT.ValueSpecTextarea, + string +> {} diff --git a/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.html b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.html new file mode 100644 index 000000000..73d116f92 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.html @@ -0,0 +1,11 @@ +{{ spec.name }} + + diff --git a/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts new file mode 100644 index 000000000..6a3c0196f --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core' +import { CT } from '@start9labs/start-sdk' +import { Control } from '../control' + +@Component({ + selector: 'form-toggle', + templateUrl: './form-toggle.component.html', + host: { class: 'g-toggle' }, +}) +export class FormToggleComponent extends Control {} diff --git a/web/projects/ui/src/app/components/form/form-union/form-union.component.html b/web/projects/ui/src/app/components/form/form-union/form-union.component.html new file mode 100644 index 000000000..1cb5bfe57 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-union/form-union.component.html @@ -0,0 +1,11 @@ + + + + diff --git a/web/projects/ui/src/app/components/form/form-union/form-union.component.scss b/web/projects/ui/src/app/components/form/form-union/form-union.component.scss new file mode 100644 index 000000000..cfb2f95e8 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-union/form-union.component.scss @@ -0,0 +1,8 @@ +:host { + display: block; +} + +.group { + display: block; + margin-top: 1rem; +} diff --git a/web/projects/ui/src/app/components/form/form-union/form-union.component.ts b/web/projects/ui/src/app/components/form/form-union/form-union.component.ts new file mode 100644 index 000000000..2c164be48 --- /dev/null +++ b/web/projects/ui/src/app/components/form/form-union/form-union.component.ts @@ -0,0 +1,55 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + Input, + OnChanges, +} from '@angular/core' +import { ControlContainer, FormGroupName } from '@angular/forms' +import { CT } from '@start9labs/start-sdk' +import { FormService } from 'src/app/services/form.service' +import { tuiPure } from '@taiga-ui/cdk' + +@Component({ + selector: 'form-union', + templateUrl: './form-union.component.html', + styleUrls: ['./form-union.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + viewProviders: [ + { + provide: ControlContainer, + useExisting: FormGroupName, + }, + ], +}) +export class FormUnionComponent implements OnChanges { + @Input() + spec!: CT.ValueSpecUnion + + selectSpec!: CT.ValueSpecSelect + + private readonly form = inject(FormGroupName) + private readonly formService = inject(FormService) + + get union(): string { + return this.form.value.selection + } + + @tuiPure + onUnion(union: string) { + this.form.control.setControl( + 'value', + this.formService.getFormGroup( + union ? this.spec.variants[union].spec : {}, + ), + { + emitEvent: false, + }, + ) + } + + ngOnChanges() { + this.selectSpec = this.formService.getUnionSelectSpec(this.spec, this.union) + if (this.union) this.onUnion(this.union) + } +} diff --git a/web/projects/ui/src/app/components/form/form.module.ts b/web/projects/ui/src/app/components/form/form.module.ts new file mode 100644 index 000000000..fe16b229f --- /dev/null +++ b/web/projects/ui/src/app/components/form/form.module.ts @@ -0,0 +1,109 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { FormsModule, ReactiveFormsModule } from '@angular/forms' +import { MaskitoModule } from '@maskito/angular' +import { TuiMapperPipeModule, TuiValueChangesModule } from '@taiga-ui/cdk' +import { + TuiErrorModule, + TuiExpandModule, + TuiHintModule, + TuiLinkModule, + TuiModeModule, + TuiTextfieldControllerModule, + TuiTooltipModule, +} from '@taiga-ui/core' +import { + TuiAppearanceModule, + TuiButtonModule, + TuiIconModule, +} from '@taiga-ui/experimental' +import { + TuiElasticContainerModule, + TuiFieldErrorPipeModule, + TuiInputDateModule, + TuiInputDateTimeModule, + TuiInputFilesModule, + TuiInputModule, + TuiInputNumberModule, + TuiInputTimeModule, + TuiMultiSelectModule, + TuiPromptModule, + TuiSelectModule, + TuiTagModule, + TuiTextAreaModule, + TuiToggleModule, +} from '@taiga-ui/kit' + +import { FormGroupComponent } from './form-group/form-group.component' +import { FormTextComponent } from './form-text/form-text.component' +import { FormToggleComponent } from './form-toggle/form-toggle.component' +import { FormTextareaComponent } from './form-textarea/form-textarea.component' +import { FormNumberComponent } from './form-number/form-number.component' +import { FormSelectComponent } from './form-select/form-select.component' +import { FormFileComponent } from './form-file/form-file.component' +import { FormMultiselectComponent } from './form-multiselect/form-multiselect.component' +import { FormUnionComponent } from './form-union/form-union.component' +import { FormObjectComponent } from './form-object/form-object.component' +import { FormArrayComponent } from './form-array/form-array.component' +import { FormControlComponent } from './form-control/form-control.component' +import { MustachePipe } from './mustache.pipe' +import { ControlDirective } from './control.directive' +import { FormColorComponent } from './form-color/form-color.component' +import { FormDatetimeComponent } from './form-datetime/form-datetime.component' +import { HintPipe } from './hint.pipe' + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + TuiInputModule, + TuiInputNumberModule, + TuiInputFilesModule, + TuiTextAreaModule, + TuiSelectModule, + TuiMultiSelectModule, + TuiToggleModule, + TuiTooltipModule, + TuiHintModule, + TuiModeModule, + TuiTagModule, + TuiButtonModule, + TuiExpandModule, + TuiTextfieldControllerModule, + TuiLinkModule, + TuiPromptModule, + TuiErrorModule, + TuiFieldErrorPipeModule, + TuiValueChangesModule, + TuiElasticContainerModule, + MaskitoModule, + TuiIconModule, + TuiAppearanceModule, + TuiInputDateModule, + TuiInputTimeModule, + TuiInputDateTimeModule, + TuiMapperPipeModule, + ], + declarations: [ + FormGroupComponent, + FormControlComponent, + FormColorComponent, + FormDatetimeComponent, + FormTextComponent, + FormToggleComponent, + FormTextareaComponent, + FormNumberComponent, + FormSelectComponent, + FormMultiselectComponent, + FormFileComponent, + FormUnionComponent, + FormObjectComponent, + FormArrayComponent, + MustachePipe, + HintPipe, + ControlDirective, + ], + exports: [FormGroupComponent], +}) +export class FormModule {} diff --git a/web/projects/ui/src/app/components/form/hint.pipe.ts b/web/projects/ui/src/app/components/form/hint.pipe.ts new file mode 100644 index 000000000..b5a730661 --- /dev/null +++ b/web/projects/ui/src/app/components/form/hint.pipe.ts @@ -0,0 +1,21 @@ +import { Pipe, PipeTransform } from '@angular/core' +import { CT } from '@start9labs/start-sdk' + +@Pipe({ + name: 'hint', +}) +export class HintPipe implements PipeTransform { + transform(spec: CT.ValueSpec): string { + const hint = [] + + if (spec.description) { + hint.push(spec.description) + } + + if ('disabled' in spec && typeof spec.disabled === 'string') { + hint.push(`Disabled: ${spec.disabled}`) + } + + return hint.join('\n\n') + } +} diff --git a/web/projects/ui/src/app/components/form/invalid.service.ts b/web/projects/ui/src/app/components/form/invalid.service.ts new file mode 100644 index 000000000..9f474e853 --- /dev/null +++ b/web/projects/ui/src/app/components/form/invalid.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core' +import { ControlDirective } from './control.directive' + +@Injectable() +export class InvalidService { + private readonly controls: ControlDirective[] = [] + + scrollIntoView() { + this.controls.find(({ invalid }) => invalid)?.scrollIntoView() + } + + add(control: ControlDirective) { + this.controls.push(control) + } + + remove(control: ControlDirective) { + this.controls.splice(this.controls.indexOf(control), 1) + } +} diff --git a/web/projects/ui/src/app/components/form/mustache.pipe.ts b/web/projects/ui/src/app/components/form/mustache.pipe.ts new file mode 100644 index 000000000..ec04b0104 --- /dev/null +++ b/web/projects/ui/src/app/components/form/mustache.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core' + +const Mustache = require('mustache') + +@Pipe({ + name: 'mustache', +}) +export class MustachePipe implements PipeTransform { + transform(value: any, displayAs: string): string { + return displayAs && Mustache.render(displayAs, value) + } +} diff --git a/web/projects/ui/src/app/components/logs/logs.component.ts b/web/projects/ui/src/app/components/logs/logs.component.ts index 3d31313cb..25897c219 100644 --- a/web/projects/ui/src/app/components/logs/logs.component.ts +++ b/web/projects/ui/src/app/components/logs/logs.component.ts @@ -1,5 +1,15 @@ import { Component, Input, ViewChild } from '@angular/core' -import { IonContent, LoadingController } from '@ionic/angular' +import { IonContent } from '@ionic/angular' +import { + DownloadHTMLService, + ErrorService, + LoadingService, + Log, + LogsRes, + ServerLogsReq, + toLocalIsoString, +} from '@start9labs/shared' +import { TuiDestroyService } from '@taiga-ui/cdk' import { bufferTime, catchError, @@ -11,15 +21,6 @@ import { takeUntil, tap, } from 'rxjs' -import { - LogsRes, - ServerLogsReq, - ErrorToastService, - toLocalIsoString, - Log, - DownloadHTMLService, -} from '@start9labs/shared' -import { TuiDestroyService } from '@taiga-ui/cdk' import { RR } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ConnectionService } from 'src/app/services/connection.service' @@ -66,10 +67,10 @@ export class LogsComponent { count = 0 constructor( - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly destroy$: TuiDestroyService, private readonly api: ApiService, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly downloadHtml: DownloadHTMLService, private readonly connection$: ConnectionService, ) {} @@ -97,7 +98,7 @@ export class LogsComponent { this.processRes(res) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { e.target.complete() } @@ -119,10 +120,7 @@ export class LogsComponent { } async download() { - const loader = await this.loadingCtrl.create({ - message: 'Processing 10,000 logs...', - }) - await loader.present() + const loader = this.loader.open('Processing 10,000 logs...').subscribe() try { const { entries } = await this.fetchLogs({ @@ -139,9 +137,9 @@ export class LogsComponent { this.downloadHtml.download(`${this.context}-logs.html`, html, styles) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/components/toast-container/refresh-alert/refresh-alert.component.ts b/web/projects/ui/src/app/components/toast-container/refresh-alert/refresh-alert.component.ts index f039841d3..04943eb1a 100644 --- a/web/projects/ui/src/app/components/toast-container/refresh-alert/refresh-alert.component.ts +++ b/web/projects/ui/src/app/components/toast-container/refresh-alert/refresh-alert.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' +import { SwUpdate } from '@angular/service-worker' +import { LoadingService } from '@start9labs/shared' import { merge, Observable, Subject } from 'rxjs' import { RefreshAlertService } from './refresh-alert.service' -import { SwUpdate } from '@angular/service-worker' -import { LoadingController } from '@ionic/angular' @Component({ selector: 'refresh-alert', @@ -18,7 +18,7 @@ export class RefreshAlertComponent { constructor( @Inject(RefreshAlertService) private readonly refresh$: Observable, private readonly updates: SwUpdate, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, ) {} ngOnInit() { @@ -26,17 +26,14 @@ export class RefreshAlertComponent { } async pwaReload() { - const loader = await this.loadingCtrl.create({ - message: 'Reloading PWA...', - }) - await loader.present() + const loader = this.loader.open('Reloading PWA...').subscribe() try { // attempt to update to the latest client version available await this.updates.activateUpdate() } catch (e) { console.error('Error activating update from service worker: ', e) } finally { - loader.dismiss() + loader.unsubscribe() // always reload, as this resolves most out of sync cases window.location.reload() } diff --git a/web/projects/ui/src/app/components/toast-container/update-toast/update-toast.component.ts b/web/projects/ui/src/app/components/toast-container/update-toast/update-toast.component.ts index 72b6956ee..276260ba8 100644 --- a/web/projects/ui/src/app/components/toast-container/update-toast/update-toast.component.ts +++ b/web/projects/ui/src/app/components/toast-container/update-toast/update-toast.component.ts @@ -1,10 +1,9 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' -import { LoadingController } from '@ionic/angular' -import { ErrorToastService } from '@start9labs/shared' -import { Observable, Subject, merge } from 'rxjs' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { merge, Observable, Subject } from 'rxjs' +import { ApiService } from '../../../services/api/embassy-api.service' import { UpdateToastService } from './update-toast.service' -import { ApiService } from '../../../services/api/embassy-api.service' @Component({ selector: 'update-toast', @@ -19,8 +18,8 @@ export class UpdateToastComponent { constructor( @Inject(UpdateToastService) private readonly update$: Observable, private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, - private readonly loadingCtrl: LoadingController, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, ) {} onDismiss() { @@ -30,18 +29,14 @@ export class UpdateToastComponent { async restart(): Promise { this.onDismiss() - const loader = await this.loadingCtrl.create({ - message: 'Restarting...', - }) - - await loader.present() + const loader = this.loader.open('Restarting...').subscribe() try { await this.embassyApi.restartServer({}) } catch (e: any) { - await this.errToast.present(e) + await this.errorService.handleError(e) } finally { - await loader.dismiss() + await loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/modals/app-config/app-config.module.ts b/web/projects/ui/src/app/modals/app-config/app-config.module.ts deleted file mode 100644 index fde422826..000000000 --- a/web/projects/ui/src/app/modals/app-config/app-config.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { FormsModule, ReactiveFormsModule } from '@angular/forms' -import { IonicModule } from '@ionic/angular' -import { AppConfigPage } from './app-config.page' -import { TextSpinnerComponentModule } from '@start9labs/shared' -import { FormObjectComponentModule } from 'src/app/components/form-object/form-object.component.module' - -@NgModule({ - declarations: [AppConfigPage], - imports: [ - CommonModule, - FormsModule, - IonicModule, - TextSpinnerComponentModule, - FormObjectComponentModule, - ReactiveFormsModule, - ], - exports: [AppConfigPage], -}) -export class AppConfigPageModule {} diff --git a/web/projects/ui/src/app/modals/app-config/app-config.page.html b/web/projects/ui/src/app/modals/app-config/app-config.page.html deleted file mode 100644 index 42eb6cf39..000000000 --- a/web/projects/ui/src/app/modals/app-config/app-config.page.html +++ /dev/null @@ -1,144 +0,0 @@ - - - Config - - - - - - - - - - - - - - - - - {{ loadingError }} - - - - - - -

- - {{ manifest.title }} has been automatically configured with - recommended defaults. Make whatever changes you want, then click - "Save". - -

-
- -

- - New config options! To accept the default values, click "Save". - You may also customize these new options below. - -

-
-
- - - - -

- - - {{ manifest.title }} - -

-

- - The following modifications have been made to {{ manifest.title }} - to satisfy {{ dependentInfo.title }}: -

    -
  • -
- To accept these modifications, click "Save". - -

-
-
- - - - -

- No config options for {{ manifest.title }} {{ manifest.version }}. -

-
-
- - -
- -
-
-
-
- - - - - - - - Reset Defaults - - - - - Save - - - Close - - - - - diff --git a/web/projects/ui/src/app/modals/app-config/app-config.page.scss b/web/projects/ui/src/app/modals/app-config/app-config.page.scss deleted file mode 100644 index e568528a8..000000000 --- a/web/projects/ui/src/app/modals/app-config/app-config.page.scss +++ /dev/null @@ -1,12 +0,0 @@ -.notifier-item { - margin: 12px; - margin-top: 0px; - border-radius: 12px; - // kills the lines - --border-width: 0; - --inner-border-width: 0; -} - -.header-details { - font-size: 20px; -} \ No newline at end of file diff --git a/web/projects/ui/src/app/modals/app-config/app-config.page.ts b/web/projects/ui/src/app/modals/app-config/app-config.page.ts deleted file mode 100644 index 4bba83acb..000000000 --- a/web/projects/ui/src/app/modals/app-config/app-config.page.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { Component, Input } from '@angular/core' -import { - AlertController, - ModalController, - LoadingController, - IonicSafeString, -} from '@ionic/angular' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { - ErrorToastService, - getErrorMessage, - isEmptyObject, - isObject, -} from '@start9labs/shared' -import { DependentInfo } from 'src/app/types/dependent-info' -import { ConfigSpec } from 'src/app/pkg-config/config-types' -import { - DataModel, - InstalledState, - PackageDataEntry, -} from 'src/app/services/patch-db/data-model' -import { PatchDB } from 'patch-db-client' -import { UntypedFormGroup } from '@angular/forms' -import { - convertValuesRecursive, - FormService, -} from 'src/app/services/form.service' -import { compare, Operation, getValueByPointer } from 'fast-json-patch' -import { hasCurrentDeps } from 'src/app/util/has-deps' -import { - getAllPackages, - getManifest, - getPackage, - isInstalled, -} from 'src/app/util/get-package-data' -import { Breakages } from 'src/app/services/api/api.types' - -@Component({ - selector: 'app-config', - templateUrl: './app-config.page.html', - styleUrls: ['./app-config.page.scss'], -}) -export class AppConfigPage { - @Input() pkgId!: string - - @Input() dependentInfo?: DependentInfo - - pkg!: PackageDataEntry - loadingText = '' - - configSpec?: ConfigSpec - configForm?: UntypedFormGroup - - original?: object // only if existing config - diff?: string[] // only if dependent info - - loading = true - hasNewOptions = false - saving = false - loadingError: string | IonicSafeString = '' - - constructor( - private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, - private readonly loadingCtrl: LoadingController, - private readonly alertCtrl: AlertController, - private readonly modalCtrl: ModalController, - private readonly formService: FormService, - private readonly patch: PatchDB, - ) {} - - get hasConfig() { - return this.pkg.stateInfo.manifest.hasConfig - } - - async ngOnInit() { - try { - const pkg = await getPackage(this.patch, this.pkgId) - if (!pkg || !isInstalled(pkg)) return - - this.pkg = pkg - - if (!this.hasConfig) return - - let newConfig: object | undefined - let patch: Operation[] | undefined - - if (this.dependentInfo) { - this.loadingText = `Setting properties to accommodate ${this.dependentInfo.title}` - const { - oldConfig: oc, - newConfig: nc, - spec: s, - } = await this.embassyApi.dryConfigureDependency({ - dependencyId: this.pkgId, - dependentId: this.dependentInfo.id, - }) - this.original = oc - newConfig = nc - this.configSpec = s - patch = compare(this.original, newConfig) - } else { - this.loadingText = 'Loading Config' - const { config: c, spec: s } = await this.embassyApi.getPackageConfig({ - id: this.pkgId, - }) - this.original = c - this.configSpec = s - } - - this.configForm = this.formService.createForm( - this.configSpec, - newConfig || this.original, - ) - - if (patch) { - this.diff = this.getDiff(patch) - this.markDirty(patch) - } - } catch (e: any) { - this.loadingError = getErrorMessage(e) - } finally { - this.loading = false - } - } - - resetDefaults() { - this.configForm = this.formService.createForm(this.configSpec!) - const patch = compare(this.original || {}, this.configForm.value) - this.markDirty(patch) - } - - async dismiss() { - if (this.configForm?.dirty) { - this.presentAlertUnsaved() - } else { - this.modalCtrl.dismiss() - } - } - - async tryConfigure() { - convertValuesRecursive(this.configSpec!, this.configForm!) - - if (this.configForm!.invalid) { - document - .getElementsByClassName('validation-error')[0] - ?.scrollIntoView({ behavior: 'smooth' }) - return - } - - this.saving = true - - if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patch))) { - this.dryConfigure() - } else { - this.configure() - } - } - - private async dryConfigure() { - const loader = await this.loadingCtrl.create({ - message: 'Checking dependent services...', - }) - await loader.present() - - try { - const breakages = await this.embassyApi.drySetPackageConfig({ - id: this.pkgId, - config: this.configForm!.value, - }) - - if (isEmptyObject(breakages)) { - this.configure(loader) - } else { - await loader.dismiss() - const proceed = await this.presentAlertBreakages(breakages) - if (proceed) { - this.configure() - } else { - this.saving = false - } - } - } catch (e: any) { - this.errToast.present(e) - this.saving = false - loader.dismiss() - } - } - - private async configure(loader?: HTMLIonLoadingElement) { - const message = 'Saving...' - if (loader) { - loader.message = message - } else { - loader = await this.loadingCtrl.create({ message }) - await loader.present() - } - - try { - await this.embassyApi.setPackageConfig({ - id: this.pkgId, - config: this.configForm!.value, - }) - this.modalCtrl.dismiss() - } catch (e: any) { - this.errToast.present(e) - } finally { - this.saving = false - loader.dismiss() - } - } - - private async presentAlertBreakages(breakages: Breakages): Promise { - let message: string = - 'As a result of this change, the following services will no longer work properly and may crash:
    ' - const localPkgs = await getAllPackages(this.patch) - const bullets = Object.keys(breakages).map(id => { - const title = getManifest(localPkgs[id]).title - return `
  • ${title}
  • ` - }) - message = `${message}${bullets}
` - - return new Promise(async resolve => { - const alert = await this.alertCtrl.create({ - header: 'Warning', - message, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - handler: () => { - resolve(false) - }, - }, - { - text: 'Continue', - handler: () => { - resolve(true) - }, - cssClass: 'enter-click', - }, - ], - cssClass: 'alert-warning-message', - }) - - await alert.present() - }) - } - - private getDiff(patch: Operation[]): string[] { - return patch.map(op => { - let message: string - switch (op.op) { - case 'add': - message = `Added ${this.getNewValue(op.value)}` - break - case 'remove': - message = `Removed ${this.getOldValue(op.path)}` - break - case 'replace': - message = `Changed from ${this.getOldValue( - op.path, - )} to ${this.getNewValue(op.value)}` - break - default: - message = `Unknown operation` - } - - let displayPath: string - - const arrPath = op.path - .substring(1) - .split('/') - .map(node => { - const num = Number(node) - return isNaN(num) ? node : num - }) - - if (typeof arrPath[arrPath.length - 1] === 'number') { - arrPath.pop() - } - - displayPath = arrPath.join(' → ') - - return `${displayPath}: ${message}` - }) - } - - private getOldValue(path: any): string { - const val = getValueByPointer(this.original, path) - if (['string', 'number', 'boolean'].includes(typeof val)) { - return val - } else if (isObject(val)) { - return 'entry' - } else { - return 'list' - } - } - - private getNewValue(val: any): string { - if (['string', 'number', 'boolean'].includes(typeof val)) { - return val - } else if (isObject(val)) { - return 'new entry' - } else { - return 'new list' - } - } - - private markDirty(patch: Operation[]) { - patch.forEach(op => { - const arrPath = op.path - .substring(1) - .split('/') - .map(node => { - const num = Number(node) - return isNaN(num) ? node : num - }) - - if (op.op !== 'remove') this.configForm!.get(arrPath)?.markAsDirty() - - if (typeof arrPath[arrPath.length - 1] === 'number') { - const prevPath = arrPath.slice(0, arrPath.length - 1) - this.configForm!.get(prevPath)?.markAsDirty() - } - }) - } - - private async presentAlertUnsaved() { - const alert = await this.alertCtrl.create({ - header: 'Unsaved Changes', - message: 'You have unsaved changes. Are you sure you want to leave?', - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: `Leave`, - handler: () => { - this.modalCtrl.dismiss() - }, - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } -} diff --git a/web/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts b/web/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts index 15bec3d4f..e1d637ecf 100644 --- a/web/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts +++ b/web/projects/ui/src/app/modals/app-recover-select/app-recover-select.page.ts @@ -1,16 +1,12 @@ import { Component, Input } from '@angular/core' -import { - LoadingController, - ModalController, - IonicSafeString, -} from '@ionic/angular' -import { getErrorMessage } from '@start9labs/shared' +import { IonicSafeString, ModalController } from '@ionic/angular' +import { getErrorMessage, LoadingService } from '@start9labs/shared' +import { PatchDB } from 'patch-db-client' +import { take } from 'rxjs' import { BackupInfo } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { PatchDB } from 'patch-db-client' -import { AppRecoverOption } from './to-options.pipe' import { DataModel } from 'src/app/services/patch-db/data-model' -import { take } from 'rxjs' +import { AppRecoverOption } from './to-options.pipe' @Component({ selector: 'app-recover-select', @@ -30,7 +26,7 @@ export class AppRecoverSelectPage { constructor( private readonly modalCtrl: ModalController, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly embassyApi: ApiService, private readonly patch: PatchDB, ) {} @@ -45,10 +41,7 @@ export class AppRecoverSelectPage { async restore(options: AppRecoverOption[]): Promise { const ids = options.filter(({ checked }) => !!checked).map(({ id }) => id) - const loader = await this.loadingCtrl.create({ - message: 'Initializing...', - }) - await loader.present() + const loader = this.loader.open('Initializing...').subscribe() try { await this.embassyApi.restorePackages({ @@ -61,7 +54,7 @@ export class AppRecoverSelectPage { } catch (e: any) { this.error = getErrorMessage(e) } finally { - loader.dismiss() + loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/modals/config-dep.component.ts b/web/projects/ui/src/app/modals/config-dep.component.ts new file mode 100644 index 000000000..14becf2e8 --- /dev/null +++ b/web/projects/ui/src/app/modals/config-dep.component.ts @@ -0,0 +1,104 @@ +import { + ChangeDetectionStrategy, + Component, + Input, + OnChanges, +} from '@angular/core' +import { compare, getValueByPointer, Operation } from 'fast-json-patch' +import { isObject } from '@start9labs/shared' +import { tuiIsNumber } from '@taiga-ui/cdk' +import { CommonModule } from '@angular/common' +import { TuiNotificationModule } from '@taiga-ui/core' + +@Component({ + selector: 'config-dep', + template: ` + +

+ {{ package }} +

+ The following modifications have been made to {{ package }} to satisfy + {{ dep }}: +
    +
  • +
+ To accept these modifications, click "Save". +
+ `, + standalone: true, + imports: [CommonModule, TuiNotificationModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ConfigDepComponent implements OnChanges { + @Input() + package = '' + + @Input() + dep = '' + + @Input() + original: object = {} + + @Input() + value: object = {} + + diff: string[] = [] + + ngOnChanges() { + this.diff = compare(this.original, this.value).map( + op => `${this.getPath(op)}: ${this.getMessage(op)}`, + ) + } + + private getPath(operation: Operation): string { + const path = operation.path + .substring(1) + .split('/') + .map(node => { + const num = Number(node) + return isNaN(num) ? node : num + }) + + if (tuiIsNumber(path[path.length - 1])) { + path.pop() + } + + return path.join(' → ') + } + + private getMessage(operation: Operation): string { + switch (operation.op) { + case 'add': + return `Added ${this.getNewValue(operation.value)}` + case 'remove': + return `Removed ${this.getOldValue(operation.path)}` + case 'replace': + return `Changed from ${this.getOldValue( + operation.path, + )} to ${this.getNewValue(operation.value)}` + default: + return `Unknown operation` + } + } + + private getOldValue(path: any): string { + const val = getValueByPointer(this.original, path) + if (['string', 'number', 'boolean'].includes(typeof val)) { + return val + } else if (isObject(val)) { + return 'entry' + } else { + return 'list' + } + } + + private getNewValue(val: any): string { + if (['string', 'number', 'boolean'].includes(typeof val)) { + return val + } else if (isObject(val)) { + return 'new entry' + } else { + return 'new list' + } + } +} diff --git a/web/projects/ui/src/app/modals/config.component.ts b/web/projects/ui/src/app/modals/config.component.ts new file mode 100644 index 000000000..194302f5f --- /dev/null +++ b/web/projects/ui/src/app/modals/config.component.ts @@ -0,0 +1,281 @@ +import { CommonModule } from '@angular/common' +import { Component, Inject, ViewChild } from '@angular/core' +import { + ErrorService, + getErrorMessage, + isEmptyObject, + LoadingService, +} from '@start9labs/shared' +import { CT } from '@start9labs/start-sdk' +import { TuiButtonModule } from '@taiga-ui/experimental' +import { + TuiDialogContext, + TuiDialogService, + TuiLoaderModule, + TuiModeModule, + TuiNotificationModule, +} from '@taiga-ui/core' +import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' +import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus' +import { compare, Operation } from 'fast-json-patch' +import { PatchDB } from 'patch-db-client' +import { endWith, firstValueFrom, Subscription } from 'rxjs' +import { ActionButton, FormComponent } from 'src/app/components/form.component' +import { InvalidService } from 'src/app/components/form/invalid.service' +import { ConfigDepComponent } from 'src/app/modals/config-dep.component' +import { UiPipeModule } from 'src/app/pipes/ui/ui.module' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { + DataModel, + PackageDataEntry, +} from 'src/app/services/patch-db/data-model' +import { + getAllPackages, + getManifest, + getPackage, +} from 'src/app/util/get-package-data' +import { hasCurrentDeps } from 'src/app/util/has-deps' +import { Breakages } from 'src/app/services/api/api.types' +import { DependentInfo } from 'src/app/types/dependent-info' + +export interface PackageConfigData { + readonly pkgId: string + readonly dependentInfo?: DependentInfo +} + +@Component({ + template: ` + + + +
+
+ + + + {{ manifest.title }} has been automatically configured with recommended + defaults. Make whatever changes you want, then click "Save". + + + + + + No config options for {{ manifest.title }} {{ manifest.version }}. + + + + + + + `, + styles: [ + ` + tui-notification { + font-size: 1rem; + margin-bottom: 1rem; + } + `, + ], + standalone: true, + imports: [ + CommonModule, + FormComponent, + TuiLoaderModule, + TuiNotificationModule, + TuiButtonModule, + TuiModeModule, + ConfigDepComponent, + UiPipeModule, + ], + providers: [InvalidService], +}) +export class ConfigModal { + @ViewChild(FormComponent) + private readonly form?: FormComponent> + + readonly pkgId = this.context.data.pkgId + readonly dependentInfo = this.context.data.dependentInfo + + loadingError = '' + loadingText = this.dependentInfo + ? `Setting properties to accommodate ${this.dependentInfo.title}` + : 'Loading Config' + + pkg?: PackageDataEntry + spec: CT.InputSpec = {} + patch: Operation[] = [] + buttons: ActionButton[] = [ + { + text: 'Save', + handler: value => this.save(value), + }, + ] + + original: object | null = null + value: object | null = null + + constructor( + @Inject(POLYMORPHEUS_CONTEXT) + private readonly context: TuiDialogContext, + private readonly dialogs: TuiDialogService, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, + private readonly embassyApi: ApiService, + private readonly patchDb: PatchDB, + ) {} + + get success(): boolean { + return ( + !!this.form && + !this.form.form.dirty && + !this.original && + !this.pkg?.status?.configured + ) + } + + async ngOnInit() { + try { + this.pkg = await getPackage(this.patchDb, this.pkgId) + + if (!this.pkg) { + this.loadingError = 'This service does not exist' + + return + } + + if (this.dependentInfo) { + const depConfig = await this.embassyApi.dryConfigureDependency({ + dependencyId: this.pkgId, + dependentId: this.dependentInfo.id, + }) + + this.original = depConfig.oldConfig + this.value = depConfig.newConfig || this.original + this.spec = depConfig.spec + this.patch = compare(this.original, this.value) + } else { + const { config, spec } = await this.embassyApi.getPackageConfig({ + id: this.pkgId, + }) + + this.original = config + this.value = config + this.spec = spec + } + } catch (e: any) { + this.loadingError = String(getErrorMessage(e)) + } finally { + this.loadingText = '' + } + } + + private async save(config: any) { + const loader = new Subscription() + + try { + await this.uploadFiles(config, loader) + + if (hasCurrentDeps(this.pkgId, await getAllPackages(this.patchDb))) { + await this.configureDeps(config, loader) + } else { + await this.configure(config, loader) + } + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } + + private async uploadFiles(config: Record, loader: Subscription) { + loader.unsubscribe() + loader.closed = false + + // TODO: Could be nested files + const keys = Object.keys(config).filter(key => config[key] instanceof File) + const message = `Uploading File${keys.length > 1 ? 's' : ''}...` + + if (!keys.length) return + + loader.add(this.loader.open(message).subscribe()) + + const hashes = await Promise.all( + keys.map(key => this.embassyApi.uploadFile(config[key])), + ) + keys.forEach((key, i) => (config[key] = hashes[i])) + } + + private async configureDeps( + config: Record, + loader: Subscription, + ) { + loader.unsubscribe() + loader.closed = false + loader.add(this.loader.open('Checking dependent services...').subscribe()) + + const breakages = await this.embassyApi.drySetPackageConfig({ + id: this.pkgId, + config, + }) + + loader.unsubscribe() + loader.closed = false + + if (isEmptyObject(breakages) || (await this.approveBreakages(breakages))) { + await this.configure(config, loader) + } + } + + private async configure(config: Record, loader: Subscription) { + loader.unsubscribe() + loader.closed = false + loader.add(this.loader.open('Saving...').subscribe()) + + await this.embassyApi.setPackageConfig({ id: this.pkgId, config }) + this.context.$implicit.complete() + } + + private async approveBreakages(breakages: Breakages): Promise { + const packages = await getAllPackages(this.patchDb) + const message = + 'As a result of this change, the following services will no longer work properly and may crash:
    ' + const content = `${message}${Object.keys(breakages).map( + id => `
  • ${getManifest(packages[id]).title}
  • `, + )}
` + const data: TuiPromptData = { content, yes: 'Continue', no: 'Cancel' } + + return firstValueFrom( + this.dialogs.open(TUI_PROMPT, { data }).pipe(endWith(false)), + ) + } +} diff --git a/web/projects/ui/src/app/modals/enum-list/enum-list.module.ts b/web/projects/ui/src/app/modals/enum-list/enum-list.module.ts deleted file mode 100644 index a0acb46d5..000000000 --- a/web/projects/ui/src/app/modals/enum-list/enum-list.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { EnumListPage } from './enum-list.page' -import { FormsModule } from '@angular/forms' - -@NgModule({ - declarations: [EnumListPage], - imports: [CommonModule, IonicModule, FormsModule], - exports: [EnumListPage], -}) -export class EnumListPageModule {} diff --git a/web/projects/ui/src/app/modals/enum-list/enum-list.page.html b/web/projects/ui/src/app/modals/enum-list/enum-list.page.html deleted file mode 100644 index 5cc74ba21..000000000 --- a/web/projects/ui/src/app/modals/enum-list/enum-list.page.html +++ /dev/null @@ -1,45 +0,0 @@ - - - {{ spec.name }} - - - - - - - - - - - - - - {{ selectAll ? 'Select All' : 'Deselect All' }} - - - - - {{ spec.spec['value-names'][option.key] }} - - - - - - - - - - Done - - - - diff --git a/web/projects/ui/src/app/modals/enum-list/enum-list.page.scss b/web/projects/ui/src/app/modals/enum-list/enum-list.page.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/projects/ui/src/app/modals/enum-list/enum-list.page.ts b/web/projects/ui/src/app/modals/enum-list/enum-list.page.ts deleted file mode 100644 index e5ddc8ed3..000000000 --- a/web/projects/ui/src/app/modals/enum-list/enum-list.page.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Component, Input } from '@angular/core' -import { ModalController } from '@ionic/angular' -import { ValueSpecListOf } from 'src/app/pkg-config/config-types' - -@Component({ - selector: 'enum-list', - templateUrl: './enum-list.page.html', - styleUrls: ['./enum-list.page.scss'], -}) -export class EnumListPage { - @Input() key!: string - @Input() spec!: ValueSpecListOf<'enum'> - @Input() current: string[] = [] - - options: { [option: string]: boolean } = {} - selectAll = false - - constructor(private readonly modalCtrl: ModalController) {} - - ngOnInit() { - for (let val of this.spec.spec.values || []) { - this.options[val] = this.current.includes(val) - } - // if none are selected, set selectAll to true - this.selectAll = Object.values(this.options).some(k => !k) - } - - dismiss() { - this.modalCtrl.dismiss() - } - - save() { - this.modalCtrl.dismiss( - Object.keys(this.options).filter(key => this.options[key]), - ) - } - - toggleSelectAll() { - Object.keys(this.options).forEach(k => (this.options[k] = this.selectAll)) - this.selectAll = !this.selectAll - } - - toggleSelected(key: string) { - this.options[key] = !this.options[key] - } - - asIsOrder() { - return 0 - } -} diff --git a/web/projects/ui/src/app/modals/generic-form/generic-form.module.ts b/web/projects/ui/src/app/modals/generic-form/generic-form.module.ts deleted file mode 100644 index f278f652b..000000000 --- a/web/projects/ui/src/app/modals/generic-form/generic-form.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { GenericFormPage } from './generic-form.page' -import { FormsModule, ReactiveFormsModule } from '@angular/forms' -import { FormObjectComponentModule } from 'src/app/components/form-object/form-object.component.module' - -@NgModule({ - declarations: [GenericFormPage], - imports: [ - CommonModule, - IonicModule, - FormsModule, - ReactiveFormsModule, - FormObjectComponentModule, - ], - exports: [GenericFormPage], -}) -export class GenericFormPageModule {} diff --git a/web/projects/ui/src/app/modals/generic-form/generic-form.page.html b/web/projects/ui/src/app/modals/generic-form/generic-form.page.html deleted file mode 100644 index 706c8487d..000000000 --- a/web/projects/ui/src/app/modals/generic-form/generic-form.page.html +++ /dev/null @@ -1,35 +0,0 @@ - - - {{ title }} - - - - - - - - - -
- - -
-
- - - - - - {{ button.text }} - - - - diff --git a/web/projects/ui/src/app/modals/generic-form/generic-form.page.scss b/web/projects/ui/src/app/modals/generic-form/generic-form.page.scss deleted file mode 100644 index 0353411b3..000000000 --- a/web/projects/ui/src/app/modals/generic-form/generic-form.page.scss +++ /dev/null @@ -1,9 +0,0 @@ -button:disabled, -button[disabled]{ - border: 1px solid #999999; - background-color: #cccccc; - color: #666666; -} -button { - color: var(--ion-color-primary); -} \ No newline at end of file diff --git a/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts b/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts deleted file mode 100644 index e74c65d47..000000000 --- a/web/projects/ui/src/app/modals/generic-form/generic-form.page.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Component, Input } from '@angular/core' -import { UntypedFormGroup } from '@angular/forms' -import { ModalController } from '@ionic/angular' -import { - convertValuesRecursive, - FormService, -} from 'src/app/services/form.service' -import { ConfigSpec } from 'src/app/pkg-config/config-types' - -export interface ActionButton { - text: string - handler: (value: any) => Promise - isSubmit?: boolean -} - -@Component({ - selector: 'generic-form', - templateUrl: './generic-form.page.html', - styleUrls: ['./generic-form.page.scss'], -}) -export class GenericFormPage { - @Input() title!: string - @Input() spec!: ConfigSpec - @Input() buttons!: ActionButton[] - @Input() initialValue: object = {} - - submitBtn!: ActionButton - formGroup!: UntypedFormGroup - - constructor( - private readonly modalCtrl: ModalController, - private readonly formService: FormService, - ) {} - - ngOnInit() { - this.formGroup = this.formService.createForm(this.spec, this.initialValue) - this.submitBtn = this.buttons.find(btn => btn.isSubmit) || { - text: '', - handler: () => Promise.resolve(true), - } - } - - async dismiss(): Promise { - this.modalCtrl.dismiss() - } - - async handleClick(handler: ActionButton['handler']): Promise { - convertValuesRecursive(this.spec, this.formGroup) - - if (this.formGroup.invalid) { - document - .getElementsByClassName('validation-error')[0] - ?.scrollIntoView({ behavior: 'smooth' }) - return - } - - const success = await handler(this.formGroup.value) - if (success !== false) this.modalCtrl.dismiss() - } -} diff --git a/web/projects/ui/src/app/modals/generic-input/generic-input.component.html b/web/projects/ui/src/app/modals/generic-input/generic-input.component.html deleted file mode 100644 index 8ff7e795f..000000000 --- a/web/projects/ui/src/app/modals/generic-input/generic-input.component.html +++ /dev/null @@ -1,67 +0,0 @@ - -
- - -

{{ options.title }}

-
-

{{ options.message }}

- -
-

- {{ options.warning }} -

-
-
-
- -
-
-

{{ options.label }}

- - - - - - - -

- {{ error }} -

-
- -
- Cancel - - {{ options.buttonText }} - -
-
-
-
diff --git a/web/projects/ui/src/app/modals/generic-input/generic-input.component.module.ts b/web/projects/ui/src/app/modals/generic-input/generic-input.component.module.ts deleted file mode 100644 index d2b1faab4..000000000 --- a/web/projects/ui/src/app/modals/generic-input/generic-input.component.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { GenericInputComponent } from './generic-input.component' -import { IonicModule } from '@ionic/angular' -import { RouterModule } from '@angular/router' -import { SharedPipesModule } from '@start9labs/shared' -import { FormsModule } from '@angular/forms' - -@NgModule({ - declarations: [GenericInputComponent], - imports: [ - CommonModule, - IonicModule, - FormsModule, - RouterModule.forChild([]), - SharedPipesModule, - ], - exports: [GenericInputComponent], -}) -export class GenericInputComponentModule {} diff --git a/web/projects/ui/src/app/modals/generic-input/generic-input.component.scss b/web/projects/ui/src/app/modals/generic-input/generic-input.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/projects/ui/src/app/modals/generic-input/generic-input.component.ts b/web/projects/ui/src/app/modals/generic-input/generic-input.component.ts deleted file mode 100644 index 59ebb8c3d..000000000 --- a/web/projects/ui/src/app/modals/generic-input/generic-input.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Component, inject, Input, ViewChild } from '@angular/core' -import { ModalController, IonicSafeString, IonInput } from '@ionic/angular' -import { getErrorMessage, THEME } from '@start9labs/shared' -import { MaskPipe } from 'src/app/pipes/mask/mask.pipe' - -@Component({ - selector: 'generic-input', - templateUrl: './generic-input.component.html', - styleUrls: ['./generic-input.component.scss'], - providers: [MaskPipe], -}) -export class GenericInputComponent { - @ViewChild('mainInput') elem?: IonInput - - @Input() options!: GenericInputOptions - - value!: string - masked!: boolean - - maskedValue?: string - - error: string | IonicSafeString = '' - - readonly theme$ = inject(THEME) - - constructor( - private readonly modalCtrl: ModalController, - private readonly mask: MaskPipe, - ) {} - - ngOnInit() { - const defaultOptions: Partial = { - buttonText: 'Submit', - placeholder: 'Enter value', - nullable: false, - useMask: false, - initialValue: '', - } - this.options = { - ...defaultOptions, - ...this.options, - } - - this.masked = !!this.options.useMask - this.value = this.options.initialValue || '' - } - - ngAfterViewInit() { - setTimeout(() => this.elem?.setFocus(), 400) - } - - toggleMask() { - this.masked = !this.masked - } - - cancel() { - this.modalCtrl.dismiss() - } - - transformInput(newValue: string) { - let i = 0 - this.value = newValue - .split('') - .map(x => (x === '●' ? this.value[i++] : x)) - .join('') - this.maskedValue = this.mask.transform(this.value) - } - - async submit() { - const value = this.value.trim() - - if (!value && !this.options.nullable) return - - try { - await this.options.submitFn(value) - this.modalCtrl.dismiss(undefined, 'success') - } catch (e: any) { - this.error = getErrorMessage(e) - } - } -} - -export interface GenericInputOptions { - // required - title: string - message: string - label: string - submitFn: (value: string) => Promise - // optional - warning?: string - buttonText?: string - placeholder?: string - nullable?: boolean - useMask?: boolean - initialValue?: string -} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts deleted file mode 100644 index 096e06add..000000000 --- a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core' -import { CommonModule } from '@angular/common' -import { IonicModule } from '@ionic/angular' -import { MarketplaceSettingsPage } from './marketplace-settings.page' -import { SharedPipesModule } from '@start9labs/shared' -import { StoreIconComponentModule } from 'src/app/components/store-icon/store-icon.component.module' - -@NgModule({ - imports: [ - CommonModule, - IonicModule, - SharedPipesModule, - StoreIconComponentModule, - ], - declarations: [MarketplaceSettingsPage], -}) -export class MarketplaceSettingsPageModule {} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html index 663b022cd..ab3bf8b34 100644 --- a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html +++ b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html @@ -1,67 +1,30 @@ - - - Change Registry - - - - - - - - - - - Default Registries - +

Default Registries

+ +

Custom Registries

+ +
+ + +
+ diff --git a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss index e69de29bb..310b9ab30 100644 --- a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss +++ b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss @@ -0,0 +1,5 @@ +.connect-container { + display: flex; + flex-direction: row; + align-items: center; +} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts index f62331819..dd8fe73eb 100644 --- a/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts +++ b/web/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts @@ -1,276 +1,230 @@ -import { - ChangeDetectionStrategy, - Component, - Inject, - ViewChild, -} from '@angular/core' -import { - ActionSheetController, - AlertController, - LoadingController, - ModalController, -} from '@ionic/angular' -import { ActionSheetButton } from '@ionic/core' -import { ErrorToastService, sameUrl, toUrl } from '@start9labs/shared' +import { CommonModule } from '@angular/common' +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { AbstractMarketplaceService } from '@start9labs/marketplace' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ValueSpecObject } from 'src/app/pkg-config/config-types' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { + ErrorService, + LoadingService, + sameUrl, + toUrl, +} from '@start9labs/shared' +import { CT } from '@start9labs/start-sdk' +import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { + TuiButtonModule, + TuiCellModule, + TuiIconModule, + TuiTitleModule, +} from '@taiga-ui/experimental' +import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' +import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' import { PatchDB } from 'patch-db-client' -import { DataModel, UIStore } from 'src/app/services/patch-db/data-model' -import { MarketplaceService } from 'src/app/services/marketplace.service' +import { combineLatest, filter, firstValueFrom, Subscription } from 'rxjs' import { map } from 'rxjs/operators' -import { combineLatest, firstValueFrom } from 'rxjs' +import { FormComponent } from 'src/app/components/form.component' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { MarketplaceService } from 'src/app/services/marketplace.service' +import { DataModel, UIStore } from 'src/app/services/patch-db/data-model' + +import { MarketplaceRegistryComponent } from './registry.component' @Component({ + standalone: true, + imports: [ + CommonModule, + TuiCellModule, + TuiIconModule, + TuiTitleModule, + TuiButtonModule, + MarketplaceRegistryComponent, + ], selector: 'marketplace-settings', templateUrl: 'marketplace-settings.page.html', styleUrls: ['marketplace-settings.page.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class MarketplaceSettingsPage { - stores$ = combineLatest([ - this.marketplaceService.getKnownHosts$(), - this.marketplaceService.getSelectedHost$(), - ]).pipe( - map(([stores, selected]) => { - const toSlice = stores.map(s => ({ - ...s, - selected: sameUrl(s.url, selected.url), - })) - // 0 and 1 are prod and community - const standard = toSlice.slice(0, 2) - // 2 and beyond are alts - const alt = toSlice.slice(2) - - return { standard, alt } - }), + private readonly api = inject(ApiService) + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly formDialog = inject(FormDialogService) + private readonly dialogs = inject(TuiDialogService) + private readonly marketplace = inject( + AbstractMarketplaceService, + ) as MarketplaceService + private readonly hosts$ = inject(PatchDB).watch$( + 'ui', + 'marketplace', + 'knownHosts', ) - constructor( - private readonly api: ApiService, - private readonly loadingCtrl: LoadingController, - private readonly modalCtrl: ModalController, - private readonly errToast: ErrorToastService, - private readonly actionCtrl: ActionSheetController, - @Inject(AbstractMarketplaceService) - private readonly marketplaceService: MarketplaceService, - private readonly patch: PatchDB, - private readonly alertCtrl: AlertController, - ) {} + readonly stores$ = combineLatest([ + this.marketplace.getKnownHosts$(), + this.marketplace.getSelectedHost$(), + ]).pipe( + map(([stores, selected]) => + stores.map(s => ({ + ...s, + selected: sameUrl(s.url, selected.url), + })), + ), + // 0 and 1 are prod and community, 2 and beyond are alts + map(stores => ({ standard: stores.slice(0, 2), alt: stores.slice(2) })), + ) - async dismiss() { - this.modalCtrl.dismiss() - } - - async presentModalAdd() { + async add() { const { name, spec } = getMarketplaceValueSpec() - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: name, + + this.formDialog.open(FormComponent, { + label: name, + data: { spec, buttons: [ { text: 'Save for Later', - handler: (value: { url: string }) => { - this.saveOnly(value.url) - }, + handler: async ({ url }: { url: string }) => this.save(url), }, { text: 'Save and Connect', - handler: (value: { url: string }) => { - this.saveAndConnect(value.url) - }, + handler: async ({ url }: { url: string }) => this.save(url, true), isSubmit: true, }, ], }, - cssClass: 'alertlike-modal', }) - - await modal.present() } + delete(url: string, name: string = '') { + this.dialogs + .open(TUI_PROMPT, getPromptOptions(name)) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open('Deleting...').subscribe() + const hosts = await firstValueFrom(this.hosts$) + const filtered: { [url: string]: UIStore } = Object.keys(hosts) + .filter(key => !sameUrl(key, url)) + .reduce( + (prev, curr) => ({ + ...prev, + [curr]: hosts[curr], + }), + {}, + ) - async presentAction( - { url, name }: { url: string; name?: string }, - canDelete = false, - ) { - const buttons: ActionSheetButton[] = [ - { - text: 'Connect', - handler: () => { - this.connect(url) - }, - }, - ] - - if (canDelete) { - buttons.unshift({ - text: 'Delete', - role: 'destructive', - handler: () => { - this.presentAlertDelete(url, name!) - }, + try { + await this.api.setDbValue(['marketplace', 'knownHosts'], filtered) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } }) - } - - const action = await this.actionCtrl.create({ - header: name, - mode: 'ios', - buttons, - }) - - await action.present() } - private async presentAlertDelete(url: string, name: string) { - const alert = await this.alertCtrl.create({ - header: 'Confirm', - message: `Are you sure you want to delete ${name}?`, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Delete', - handler: () => this.delete(url), - cssClass: 'enter-click', - }, - ], - }) - - await alert.present() - } - - private async connect( + async connect( url: string, - loader?: HTMLIonLoadingElement, + loader: Subscription = new Subscription(), ): Promise { - const message = 'Changing Registry...' - if (!loader) { - loader = await this.loadingCtrl.create({ message }) - await loader.present() - } else { - loader.message = message - } + loader.unsubscribe() + loader.closed = false + loader.add(this.loader.open('Changing Registry...').subscribe()) try { - await this.api.setDbValue(['marketplace', 'selected-url'], url) + await this.api.setDbValue(['marketplace', 'selectedUrl'], url) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() - this.dismiss() + loader.unsubscribe() } } - private async saveOnly(rawUrl: string): Promise { - const loader = await this.loadingCtrl.create() + private async save(rawUrl: string, connect = false): Promise { + const loader = this.loader.open('Loading').subscribe() + const url = new URL(rawUrl).toString() try { - const url = new URL(rawUrl).toString() await this.validateAndSave(url, loader) + if (connect) await this.connect(url, loader) + return true } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) + return false } finally { - loader.dismiss() - } - } - - private async saveAndConnect(rawUrl: string): Promise { - const loader = await this.loadingCtrl.create() - - try { - const url = new URL(rawUrl).toString() - await this.validateAndSave(url, loader) - await this.connect(url, loader) - } catch (e: any) { - this.errToast.present(e) - } finally { - loader.dismiss() - this.dismiss() + loader.unsubscribe() } } private async validateAndSave( url: string, - loader: HTMLIonLoadingElement, + loader: Subscription, ): Promise { // Error on duplicates - const hosts = await firstValueFrom( - this.patch.watch$('ui', 'marketplace', 'knownHosts'), - ) + const hosts = await firstValueFrom(this.hosts$) const currentUrls = Object.keys(hosts).map(toUrl) - if (currentUrls.includes(url)) throw new Error('marketplace already added') + if (currentUrls.includes(url)) throw new Error('Marketplace already added') // Validate - loader.message = 'Validating marketplace...' - await loader.present() + loader.unsubscribe() + loader.closed = false + loader.add(this.loader.open('Validating marketplace...').subscribe()) - const { name } = await firstValueFrom( - this.marketplaceService.fetchInfo$(url), - ) + const { name } = await firstValueFrom(this.marketplace.fetchInfo$(url)) // Save - loader.message = 'Saving...' + loader.unsubscribe() + loader.closed = false + loader.add(this.loader.open('Saving...').subscribe()) - await this.api.setDbValue<{ name: string }>( - ['marketplace', 'knownHosts', url], - { name }, - ) - } - - private async delete(url: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Deleting...', - }) - await loader.present() - - const hosts = await firstValueFrom( - this.patch.watch$('ui', 'marketplace', 'knownHosts'), - ) - - const filtered: { [url: string]: UIStore } = Object.keys(hosts) - .filter(key => !sameUrl(key, url)) - .reduce((prev, curr) => { - const name = hosts[curr] - return { - ...prev, - [curr]: name, - } - }, {}) - - try { - await this.api.setDbValue<{ [url: string]: UIStore }>( - ['marketplace', 'knownHosts'], - filtered, - ) - } catch (e: any) { - this.errToast.present(e) - } finally { - loader.dismiss() - } + await this.api.setDbValue(['marketplace', 'knownHosts', url], { name }) } } -function getMarketplaceValueSpec(): ValueSpecObject { +export const MARKETPLACE_REGISTRY = new PolymorpheusComponent( + MarketplaceSettingsPage, +) + +function getMarketplaceValueSpec(): CT.ValueSpecObject { return { type: 'object', name: 'Add Custom Registry', + description: null, + warning: null, spec: { url: { - type: 'string', + type: 'text', name: 'URL', description: 'A fully-qualified URL of the custom registry', - nullable: false, + inputmode: 'url', + required: true, masked: false, - copyable: false, - pattern: `https?:\/\/[a-zA-Z0-9][a-zA-Z0-9-\.]+[a-zA-Z0-9]\.[^\s]{2,}`, - 'pattern-description': 'Must be a valid URL', + minLength: null, + maxLength: null, + patterns: [ + { + regex: `https?:\/\/[a-zA-Z0-9][a-zA-Z0-9-\.]+[a-zA-Z0-9]\.[^\s]{2,}`, + description: 'Must be a valid URL', + }, + ], placeholder: 'e.g. https://example.org', + default: null, + warning: null, + disabled: false, + immutable: false, + generate: null, }, }, } } + +function getPromptOptions( + name: string, +): Partial> { + return { + label: 'Confirm', + size: 's', + data: { + content: `Are you sure you want to delete ${name}?`, + yes: 'Delete', + no: 'Cancel', + }, + } +} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/registry.component.ts b/web/projects/ui/src/app/modals/marketplace-settings/registry.component.ts new file mode 100644 index 000000000..0441ae18f --- /dev/null +++ b/web/projects/ui/src/app/modals/marketplace-settings/registry.component.ts @@ -0,0 +1,42 @@ +import { NgIf } from '@angular/common' +import { + ChangeDetectionStrategy, + Component, + inject, + Input, +} from '@angular/core' +import { TuiIconModule, TuiTitleModule } from '@taiga-ui/experimental' +import { ConfigService } from 'src/app/services/config.service' + +import { StoreIconComponent } from './store-icon.component' + +@Component({ + standalone: true, + selector: '[registry]', + template: ` + +
+ {{ registry.name }} +
{{ registry.url }}
+
+ + + `, + styles: [':host { border-radius: 0.25rem; width: stretch; }'], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgIf, StoreIconComponent, TuiIconModule, TuiTitleModule], +}) +export class MarketplaceRegistryComponent { + readonly marketplace = inject(ConfigService).marketplace + + @Input() + registry!: { url: string; selected: boolean; name?: string } +} diff --git a/web/projects/ui/src/app/modals/marketplace-settings/store-icon.component.ts b/web/projects/ui/src/app/modals/marketplace-settings/store-icon.component.ts new file mode 100644 index 000000000..dcdfe3b2f --- /dev/null +++ b/web/projects/ui/src/app/modals/marketplace-settings/store-icon.component.ts @@ -0,0 +1,46 @@ +import { NgIf } from '@angular/common' +import { ChangeDetectionStrategy, Component, Input } from '@angular/core' +import { sameUrl } from '@start9labs/shared' + +@Component({ + standalone: true, + selector: 'store-icon', + template: ` + Marketplace Icon + + Marketplace Icon + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgIf], +}) +export class StoreIconComponent { + @Input() + url = '' + @Input() + size?: string + @Input() + marketplace!: any + + get icon() { + const { start9, community } = this.marketplace + + if (sameUrl(this.url, start9)) { + return 'assets/img/icon_transparent.png' + } else if (sameUrl(this.url, community)) { + return 'assets/img/community-store.png' + } + return null + } +} diff --git a/web/projects/ui/src/app/modals/os-update/os-update.page.ts b/web/projects/ui/src/app/modals/os-update/os-update.page.ts index 9900bbb47..45b9cb009 100644 --- a/web/projects/ui/src/app/modals/os-update/os-update.page.ts +++ b/web/projects/ui/src/app/modals/os-update/os-update.page.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' -import { LoadingController, ModalController } from '@ionic/angular' -import { ApiService } from '../../services/api/embassy-api.service' -import { ErrorToastService } from '@start9labs/shared' +import { ModalController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' import { EOSService } from 'src/app/services/eos.service' +import { ApiService } from '../../services/api/embassy-api.service' @Component({ selector: 'os-update', @@ -15,8 +15,8 @@ export class OSUpdatePage { constructor( private readonly modalCtrl: ModalController, - private readonly loadingCtrl: LoadingController, - private readonly errToast: ErrorToastService, + private readonly loader: LoadingService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, private readonly eosService: EOSService, ) {} @@ -39,18 +39,15 @@ export class OSUpdatePage { } async updateEOS() { - const loader = await this.loadingCtrl.create({ - message: 'Beginning update...', - }) - await loader.present() + const loader = this.loader.open('Beginning update...').subscribe() try { await this.embassyApi.updateServer() this.dismiss() } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/modals/prompt.component.ts b/web/projects/ui/src/app/modals/prompt.component.ts new file mode 100644 index 000000000..e2a2765f5 --- /dev/null +++ b/web/projects/ui/src/app/modals/prompt.component.ts @@ -0,0 +1,123 @@ +import { CommonModule } from '@angular/common' +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' +import { FormsModule } from '@angular/forms' +import { TuiAutoFocusModule } from '@taiga-ui/cdk' +import { TuiDialogContext, TuiTextfieldControllerModule } from '@taiga-ui/core' +import { TuiButtonModule } from '@taiga-ui/experimental' +import { TuiInputModule } from '@taiga-ui/kit' +import { + POLYMORPHEUS_CONTEXT, + PolymorpheusComponent, +} from '@tinkoff/ng-polymorpheus' + +@Component({ + standalone: true, + template: ` +

{{ options.message }}

+

{{ options.warning }}

+
+ + {{ options.label }} + * + + +
+ + +
+
+ + + + + `, + styles: [ + ` + .warning { + color: var(--tui-warning-fill); + } + + .button { + pointer-events: auto; + margin-left: 0.25rem; + } + + .masked { + -webkit-text-security: disc; + } + `, + ], + imports: [ + CommonModule, + FormsModule, + TuiInputModule, + TuiButtonModule, + TuiTextfieldControllerModule, + TuiAutoFocusModule, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PromptModal { + masked = this.options.useMask + value = this.options.initialValue || '' + + constructor( + @Inject(POLYMORPHEUS_CONTEXT) + private readonly context: TuiDialogContext, + ) {} + + get options(): PromptOptions { + return this.context.data + } + + cancel() { + this.context.$implicit.complete() + } + + submit(value: string) { + if (value || !this.options.required) { + this.context.$implicit.next(value) + } + } +} + +export const PROMPT = new PolymorpheusComponent(PromptModal) + +export interface PromptOptions { + message: string + label?: string + warning?: string + buttonText?: string + placeholder?: string + required?: boolean + useMask?: boolean + initialValue?: string | null +} diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.module.ts b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.module.ts index 5f30abc0e..84e52d15f 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.module.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.module.ts @@ -5,7 +5,6 @@ import { IonicModule } from '@ionic/angular' import { AppActionsPage, AppActionsItemComponent } from './app-actions.page' import { QRComponentModule } from 'src/app/components/qr/qr.component.module' import { SharedPipesModule } from '@start9labs/shared' -import { GenericFormPageModule } from 'src/app/modals/generic-form/generic-form.module' import { ActionSuccessPageModule } from 'src/app/modals/action-success/action-success.module' const routes: Routes = [ @@ -22,7 +21,6 @@ const routes: Routes = [ RouterModule.forChild(routes), QRComponentModule, SharedPipesModule, - GenericFormPageModule, ActionSuccessPageModule, ], declarations: [AppActionsPage, AppActionsItemComponent], diff --git a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index fc008a17a..dd4e4b36e 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -1,23 +1,24 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { ApiService } from 'src/app/services/api/embassy-api.service' +import { AlertController, ModalController, NavController } from '@ionic/angular' import { - AlertController, - LoadingController, - ModalController, - NavController, -} from '@ionic/angular' + ErrorService, + getPkgId, + isEmptyObject, + LoadingService, +} from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' import { PatchDB } from 'patch-db-client' +import { FormComponent } from 'src/app/components/form.component' +import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel, PackageDataEntry, } from 'src/app/services/patch-db/data-model' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' -import { isEmptyObject, ErrorToastService, getPkgId } from '@start9labs/shared' -import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page' -import { hasCurrentDeps } from 'src/app/util/has-deps' import { getAllPackages, getManifest } from 'src/app/util/get-package-data' -import { T } from '@start9labs/start-sdk' +import { hasCurrentDeps } from 'src/app/util/has-deps' @Component({ selector: 'app-actions', @@ -34,10 +35,11 @@ export class AppActionsPage { private readonly embassyApi: ApiService, private readonly modalCtrl: ModalController, private readonly alertCtrl: AlertController, - private readonly errToast: ErrorToastService, - private readonly loadingCtrl: LoadingController, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, private readonly navCtrl: NavController, private readonly patch: PatchDB, + private readonly formDialog: FormDialogService, ) {} async handleAction( @@ -46,23 +48,19 @@ export class AppActionsPage { ) { if (status && action.value.allowedStatuses.includes(status.main.status)) { if (!isEmptyObject(action.value.input || {})) { - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: action.value.name, + this.formDialog.open(FormComponent, { + label: action.value.name, + data: { spec: action.value.input, buttons: [ { text: 'Execute', - handler: (value: any) => { - return this.executeAction(action.key, value) - }, - isSubmit: true, + handler: async (value: any) => + this.executeAction(action.key, value), }, ], }, }) - await modal.present() } else { const alert = await this.alertCtrl.create({ header: 'Confirm', @@ -147,10 +145,7 @@ export class AppActionsPage { } private async uninstall() { - const loader = await this.loadingCtrl.create({ - message: `Beginning uninstall...`, - }) - await loader.present() + const loader = this.loader.open(`Beginning uninstall...`).subscribe() try { await this.embassyApi.uninstallPackage({ id: this.pkgId }) @@ -159,9 +154,9 @@ export class AppActionsPage { .catch(e => console.error('Failed to mark instructions as unseen', e)) this.navCtrl.navigateRoot('/services') } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -169,10 +164,7 @@ export class AppActionsPage { actionId: string, input?: object, ): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Executing action...', - }) - await loader.present() + const loader = this.loader.open('Executing action...').subscribe() try { const res = await this.embassyApi.executePackageAction({ @@ -191,10 +183,10 @@ export class AppActionsPage { setTimeout(() => successModal.present(), 500) return true // needed to dismiss original modal/alert } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) return false // don't dismiss original modal/alert } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html index 79b1a1fde..ab7940a06 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-interfaces/app-interfaces.page.html @@ -8,7 +8,9 @@ - + User Interfaces { - const sorted = Object.values(interfaces) - .sort(iface => - iface.name.toLowerCase() > iface.name.toLowerCase() ? -1 : 1, - ) - .map(iface => { - // TODO @Matt - const host = {} as any - return { - ...iface, - addresses: getAddresses(iface, host), - } - }) + private readonly serviceInterfaces$ = this.patch.watch$( + 'packageData', + this.pkgId, + 'serviceInterfaces', + ) + private readonly hosts$ = this.patch.watch$( + 'packageData', + this.pkgId, + 'hosts', + ) - return { - ui: sorted.filter(val => val.type === 'ui'), - api: sorted.filter(val => val.type === 'api'), - p2p: sorted.filter(val => val.type === 'p2p'), - } - }), - ) + readonly serviceInterfacesWithHostInfo$ = combineLatest([ + this.serviceInterfaces$, + this.hosts$, + ]).pipe( + map(([interfaces, hosts]) => { + const sorted = Object.values(interfaces) + .sort(iface => + iface.name.toLowerCase() > iface.name.toLowerCase() ? -1 : 1, + ) + .map(iface => { + return { + ...iface, + addresses: getAddresses( + iface, + hosts[iface.addressInfo.hostId] || {}, + ), + } + }) + + return { + ui: sorted.filter(val => val.type === 'ui'), + api: sorted.filter(val => val.type === 'api'), + p2p: sorted.filter(val => val.type === 'p2p'), + } + }), + ) constructor( private readonly route: ActivatedRoute, @@ -108,20 +120,15 @@ function getAddresses( host: T.Host, ): MappedAddress[] { const addressInfo = serviceInterface.addressInfo - const username = addressInfo.username ? addressInfo.username + '@' : '' - const suffix = addressInfo.suffix || '' const hostnames = - host.kind === 'multi' ? host.hostnameInfo[addressInfo.internalPort] : [] // TODO: non-multi - /* host.hostname - ? [host.hostname] - : [] */ + host.kind === 'multi' ? host.hostnameInfo[addressInfo.internalPort] : [] - return hostnames.flatMap(h => { + const addressesWithNames = hostnames.flatMap(h => { let name = '' if (h.kind === 'onion') { - name = 'Tor' + name = `Tor` } else { const hostnameKind = h.hostname.kind @@ -135,9 +142,23 @@ function getAddresses( } } - return addressHostToUrl(addressInfo, h).map(url => ({ - name, - url, - })) + const addresses = utils.addressHostToUrl(addressInfo, h) + if (addresses.length > 1) { + return utils.addressHostToUrl(addressInfo, h).map(url => ({ + name: `${name} (${new URL(url).protocol + .replace(':', '') + .toUpperCase()})`, + url, + })) + } else { + return utils.addressHostToUrl(addressInfo, h).map(url => ({ + name, + url, + })) + } }) + + return addressesWithNames.filter( + (value, index, self) => index === self.findIndex(t => t.url === value.url), + ) } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts index 8cf576bd7..61b17b669 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-metrics/app-metrics.page.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core' import { ActivatedRoute } from '@angular/router' +import { ErrorService, getPkgId, pauseFor } from '@start9labs/shared' import { Metric } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { pauseFor, ErrorToastService, getPkgId } from '@start9labs/shared' @Component({ selector: 'app-metrics', @@ -17,7 +17,7 @@ export class AppMetricsPage { constructor( private readonly route: ActivatedRoute, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, ) {} @@ -46,7 +46,7 @@ export class AppMetricsPage { try { this.metrics = await this.embassyApi.getPkgMetrics({ id: this.pkgId }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) this.stopDaemon() } finally { this.loading = false diff --git a/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts index d9b6bbc3e..af659c32b 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-properties/app-properties.page.ts @@ -1,6 +1,5 @@ import { Component, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { ApiService } from 'src/app/services/api/embassy-api.service' import { AlertController, IonBackButtonDelegate, @@ -8,18 +7,15 @@ import { NavController, ToastController, } from '@ionic/angular' -import { PackageProperties } from 'src/app/util/properties.util' -import { QRComponent } from 'src/app/components/qr/qr.component' -import { PatchDB } from 'patch-db-client' -import { DataModel } from 'src/app/services/patch-db/data-model' -import { - ErrorToastService, - getPkgId, - copyToClipboard, -} from '@start9labs/shared' +import { copyToClipboard, ErrorService, getPkgId } from '@start9labs/shared' import { TuiDestroyService } from '@taiga-ui/cdk' import { getValueByPointer } from 'fast-json-patch' +import { PatchDB } from 'patch-db-client' import { map, takeUntil } from 'rxjs/operators' +import { QRComponent } from 'src/app/components/qr/qr.component' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { PackageProperties } from 'src/app/util/properties.util' @Component({ selector: 'app-properties', @@ -47,7 +43,7 @@ export class AppPropertiesPage { constructor( private readonly route: ActivatedRoute, private readonly embassyApi: ApiService, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly alertCtrl: AlertController, private readonly toastCtrl: ToastController, private readonly modalCtrl: ModalController, @@ -139,7 +135,7 @@ export class AppPropertiesPage { }) this.node = getValueByPointer(this.properties, this.pointer) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { this.loading = false } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.module.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.module.ts index 0b2d82763..86744cc91 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.module.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.module.ts @@ -4,13 +4,11 @@ import { Routes, RouterModule } from '@angular/router' import { IonicModule } from '@ionic/angular' import { AppShowPage } from './app-show.page' import { - EmptyPipe, EmverPipesModule, ResponsiveColModule, SharedPipesModule, } from '@start9labs/shared' import { StatusComponentModule } from 'src/app/components/status/status.component.module' -import { AppConfigPageModule } from 'src/app/modals/app-config/app-config.module' import { LaunchablePipeModule } from 'src/app/pipes/launchable/launchable.module' import { UiPipeModule } from 'src/app/pipes/ui/ui.module' import { AppShowHeaderComponent } from './components/app-show-header/app-show-header.component' @@ -51,7 +49,6 @@ const routes: Routes = [ InstallingProgressPipeModule, IonicModule, RouterModule.forChild(routes), - AppConfigPageModule, EmverPipesModule, LaunchablePipeModule, UiPipeModule, diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts index 010a7473c..cb1f6f5f9 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/app-show.page.ts @@ -11,7 +11,6 @@ import { renderPkgStatus } from 'src/app/services/pkg-status-rendering.service' import { map, tap } from 'rxjs/operators' import { ActivatedRoute, NavigationExtras } from '@angular/router' import { getPkgId } from '@start9labs/shared' -import { ModalService } from 'src/app/services/modal.service' import { DependentInfo } from 'src/app/types/dependent-info' import { DepErrorService, @@ -26,6 +25,8 @@ import { isUpdating, } from 'src/app/util/get-package-data' import { T } from '@start9labs/start-sdk' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component' export interface DependencyInfo { id: string @@ -68,8 +69,8 @@ export class AppShowPage { private readonly route: ActivatedRoute, private readonly navCtrl: NavController, private readonly patch: PatchDB, - private readonly modalService: ModalService, private readonly depErrorService: DepErrorService, + private readonly formDialog: FormDialogService, ) {} showProgress( @@ -171,7 +172,13 @@ export class AppShowPage { case 'update': return this.installDep(pkg, pkgManifest, id) case 'configure': - return this.configureDep(pkgManifest, id) + return this.formDialog.open(ConfigModal, { + label: `${pkgManifest.title} config`, + data: { + pkgId: id, + dependentInfo: pkgManifest, + }, + }) } } @@ -194,19 +201,4 @@ export class AppShowPage { navigationExtras, ) } - - private async configureDep( - pkgManifest: T.Manifest, - dependencyId: string, - ): Promise { - const dependentInfo: DependentInfo = { - id: pkgManifest.id, - title: pkgManifest.title, - } - - await this.modalService.presentModalConfig({ - pkgId: dependencyId, - dependentInfo, - }) - } } diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html index 8e4bb760d..b0d65b5fd 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-health-checks/app-show-health-checks.component.html @@ -68,7 +68,7 @@ - + diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts index ba60c331d..30657ae01 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/components/app-show-status/app-show-status.component.ts @@ -1,26 +1,27 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' -import { UiLauncherService } from 'src/app/services/ui-launcher.service' -import { - PackageStatus, - PrimaryRendering, -} from 'src/app/services/pkg-status-rendering.service' +import { AlertController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' +import { PatchDB } from 'patch-db-client' +import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { ConnectionService } from 'src/app/services/connection.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' import { DataModel, PackageDataEntry, } from 'src/app/services/patch-db/data-model' -import { ErrorToastService } from '@start9labs/shared' -import { AlertController, LoadingController } from '@ionic/angular' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ModalService } from 'src/app/services/modal.service' -import { hasCurrentDeps } from 'src/app/util/has-deps' -import { ConnectionService } from 'src/app/services/connection.service' import { - isInstalled, - getManifest, + PackageStatus, + PrimaryRendering, +} from 'src/app/services/pkg-status-rendering.service' +import { UiLauncherService } from 'src/app/services/ui-launcher.service' +import { getAllPackages, + getManifest, + isInstalled, } from 'src/app/util/get-package-data' -import { PatchDB } from 'patch-db-client' -import { T } from '@start9labs/start-sdk' +import { hasCurrentDeps } from 'src/app/util/has-deps' @Component({ selector: 'app-show-status', @@ -41,11 +42,11 @@ export class AppShowStatusComponent { constructor( private readonly alertCtrl: AlertController, - private readonly errToast: ErrorToastService, - private readonly loadingCtrl: LoadingController, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, private readonly embassyApi: ApiService, private readonly launcherService: UiLauncherService, - private readonly modalService: ModalService, + private readonly formDialog: FormDialogService, readonly connection$: ConnectionService, private readonly patch: PatchDB, ) {} @@ -85,8 +86,8 @@ export class AppShowStatusComponent { } async presentModalConfig(): Promise { - return this.modalService.presentModalConfig({ - pkgId: this.manifest.id, + return this.formDialog.open(ConfigModal, { + data: { pkgId: this.manifest.id }, }) } @@ -172,47 +173,38 @@ export class AppShowStatusComponent { } private async start(): Promise { - const loader = await this.loadingCtrl.create({ - message: `Starting...`, - }) - await loader.present() + const loader = this.loader.open(`Starting...`).subscribe() try { await this.embassyApi.startPackage({ id: this.manifest.id }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async stop(): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Stopping...', - }) - await loader.present() + const loader = this.loader.open('Stopping...').subscribe() try { await this.embassyApi.stopPackage({ id: this.manifest.id }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async restart(): Promise { - const loader = await this.loadingCtrl.create({ - message: `Restarting...`, - }) - await loader.present() + const loader = this.loader.open(`Restarting...`).subscribe() try { await this.embassyApi.restartPackage({ id: this.manifest.id }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async presentAlertStart(message: string): Promise { diff --git a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts index 692d5d55f..e2a03ecf8 100644 --- a/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts +++ b/web/projects/ui/src/app/pages/apps-routes/app-show/pipes/to-buttons.pipe.ts @@ -7,11 +7,12 @@ import { InstalledState, PackageDataEntry, } from 'src/app/services/patch-db/data-model' -import { ModalService } from 'src/app/services/modal.service' import { ApiService } from 'src/app/services/api/embassy-api.service' import { from, map, Observable } from 'rxjs' import { PatchDB } from 'patch-db-client' import { T } from '@start9labs/start-sdk' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { ConfigModal, PackageConfigData } from 'src/app/modals/config.component' export interface Button { title: string @@ -30,9 +31,9 @@ export class ToButtonsPipe implements PipeTransform { private readonly route: ActivatedRoute, private readonly navCtrl: NavController, private readonly modalCtrl: ModalController, - private readonly modalService: ModalService, private readonly apiService: ApiService, private readonly patch: PatchDB, + private readonly formDialog: FormDialogService, ) {} transform(pkg: PackageDataEntry): Button[] { @@ -52,7 +53,10 @@ export class ToButtonsPipe implements PipeTransform { // config { action: async () => - this.modalService.presentModalConfig({ pkgId: manifest.id }), + this.formDialog.open(ConfigModal, { + label: `${manifest.title} configuration`, + data: { pkgId: manifest.id }, + }), title: 'Config', description: `Customize ${manifest.title}`, icon: 'options-outline', diff --git a/web/projects/ui/src/app/pages/diagnostic-routes/home/home.page.ts b/web/projects/ui/src/app/pages/diagnostic-routes/home/home.page.ts index 9bb7376bc..00ee305bc 100644 --- a/web/projects/ui/src/app/pages/diagnostic-routes/home/home.page.ts +++ b/web/projects/ui/src/app/pages/diagnostic-routes/home/home.page.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core' -import { AlertController, LoadingController } from '@ionic/angular' +import { AlertController } from '@ionic/angular' +import { LoadingService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/embassy-api.service' @Component({ @@ -18,7 +19,7 @@ export class HomePage { restarted = false constructor( - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly api: ApiService, private readonly alertCtrl: AlertController, ) {} @@ -86,10 +87,7 @@ export class HomePage { } async restart(): Promise { - const loader = await this.loadingCtrl.create({ - cssClass: 'loader', - }) - await loader.present() + const loader = this.loader.open('Loading...').subscribe() try { await this.api.diagnosticRestart() @@ -97,15 +95,12 @@ export class HomePage { } catch (e) { console.error(e) } finally { - loader.dismiss() + loader.unsubscribe() } } async forgetDrive(): Promise { - const loader = await this.loadingCtrl.create({ - cssClass: 'loader', - }) - await loader.present() + const loader = this.loader.open('Loading...').subscribe() try { await this.api.diagnosticForgetDrive() @@ -114,7 +109,7 @@ export class HomePage { } catch (e) { console.error(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -149,10 +144,7 @@ export class HomePage { } private async repairDisk(): Promise { - const loader = await this.loadingCtrl.create({ - cssClass: 'loader', - }) - await loader.present() + const loader = this.loader.open('Loading...').subscribe() try { await this.api.diagnosticRepairDisk() @@ -161,7 +153,7 @@ export class HomePage { } catch (e) { console.error(e) } finally { - loader.dismiss() + loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts b/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts index 5119b9d93..5f98627cb 100644 --- a/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts +++ b/web/projects/ui/src/app/pages/diagnostic-routes/logs/logs.page.ts @@ -1,7 +1,7 @@ import { Component, ViewChild } from '@angular/core' import { IonContent } from '@ionic/angular' +import { ErrorService, toLocalIsoString } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ErrorToastService, toLocalIsoString } from '@start9labs/shared' var Convert = require('ansi-to-html') var convert = new Convert({ @@ -23,7 +23,7 @@ export class LogsPage { constructor( private readonly api: ApiService, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, ) {} async ngOnInit() { @@ -89,7 +89,7 @@ export class LogsPage { this.needInfinite = false } } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } } } diff --git a/web/projects/ui/src/app/pages/init/init.service.ts b/web/projects/ui/src/app/pages/init/init.service.ts index 7102c9db5..245e1ae24 100644 --- a/web/projects/ui/src/app/pages/init/init.service.ts +++ b/web/projects/ui/src/app/pages/init/init.service.ts @@ -1,5 +1,5 @@ import { inject, Injectable } from '@angular/core' -import { ErrorToastService } from '@start9labs/shared' +import { ErrorService } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { catchError, @@ -24,7 +24,7 @@ interface MappedProgress { export class InitService extends Observable { private readonly state = inject(StateService) private readonly api = inject(ApiService) - private readonly errorService = inject(ErrorToastService) + private readonly errorService = inject(ErrorService) private readonly progress$ = defer(() => from(this.api.initGetProgress()), ).pipe( @@ -59,7 +59,7 @@ export class InitService extends Observable { }), catchError(e => { // @TODO this toast is presenting when we navigate away from init page. It seems other websockets exhibit the same behavior, but we never noticed because the error were not being caught and presented in this manner - this.errorService.present(e) + this.errorService.handleError(e) return EMPTY }), diff --git a/web/projects/ui/src/app/pages/login/login.page.ts b/web/projects/ui/src/app/pages/login/login.page.ts index db0d1c2c4..065a4cc7f 100644 --- a/web/projects/ui/src/app/pages/login/login.page.ts +++ b/web/projects/ui/src/app/pages/login/login.page.ts @@ -1,10 +1,11 @@ +import { DOCUMENT } from '@angular/common' import { Component, Inject } from '@angular/core' -import { getPlatforms, LoadingController } from '@ionic/angular' +import { Router } from '@angular/router' +import { getPlatforms } from '@ionic/angular' +import { LoadingService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/embassy-api.service' import { AuthService } from 'src/app/services/auth.service' -import { Router } from '@angular/router' import { ConfigService } from 'src/app/services/config.service' -import { DOCUMENT } from '@angular/common' @Component({ selector: 'login', @@ -19,7 +20,7 @@ export class LoginPage { constructor( private readonly router: Router, private readonly authService: AuthService, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly api: ApiService, public readonly config: ConfigService, @Inject(DOCUMENT) public readonly document: Document, @@ -28,10 +29,7 @@ export class LoginPage { async submit() { this.error = '' - const loader = await this.loadingCtrl.create({ - message: 'Logging in...', - }) - await loader.present() + const loader = this.loader.open('Logging in...').subscribe() try { document.cookie = '' @@ -51,7 +49,7 @@ export class LoginPage { // code 7 is for incorrect password this.error = e.code === 7 ? 'Invalid Password' : e.message } finally { - loader.dismiss() + loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts index ea1952578..c10ce42aa 100644 --- a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts +++ b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.module.ts @@ -18,7 +18,6 @@ import { import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module' import { MarketplaceStatusModule } from '../marketplace-status/marketplace-status.module' import { MarketplaceListPage } from './marketplace-list.page' -import { MarketplaceSettingsPageModule } from 'src/app/modals/marketplace-settings/marketplace-settings.module' import { StoreIconComponentModule } from 'src/app/components/store-icon/store-icon.component.module' const routes: Routes = [ @@ -43,7 +42,6 @@ const routes: Routes = [ CategoriesModule, SearchModule, SkeletonModule, - MarketplaceSettingsPageModule, StoreIconComponentModule, ResponsiveColModule, ], diff --git a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts index 6792b917a..e1f13883a 100644 --- a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts +++ b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { ModalController } from '@ionic/angular' import { AbstractMarketplaceService } from '@start9labs/marketplace' +import { TuiDialogService } from '@taiga-ui/core' import { PatchDB } from 'patch-db-client' import { map } from 'rxjs' -import { MarketplaceSettingsPage } from 'src/app/modals/marketplace-settings/marketplace-settings.page' +import { MARKETPLACE_REGISTRY } from 'src/app/modals/marketplace-settings/marketplace-settings.page' import { ConfigService } from 'src/app/services/config.service' import { MarketplaceService } from 'src/app/services/marketplace.service' import { DataModel } from 'src/app/services/patch-db/data-model' @@ -73,7 +73,7 @@ export class MarketplaceListPage { private readonly patch: PatchDB, @Inject(AbstractMarketplaceService) private readonly marketplaceService: MarketplaceService, - private readonly modalCtrl: ModalController, + private readonly dialogs: TuiDialogService, private readonly config: ConfigService, private readonly route: ActivatedRoute, ) {} @@ -82,10 +82,11 @@ export class MarketplaceListPage { query = '' async presentModalMarketplaceSettings() { - const modal = await this.modalCtrl.create({ - component: MarketplaceSettingsPage, - }) - await modal.present() + this.dialogs + .open(MARKETPLACE_REGISTRY, { + label: 'Change Registry', + }) + .subscribe() } onCategoryChange(category: string): void { diff --git a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.ts b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.ts index 4e44e2af3..5c83c536f 100644 --- a/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.ts +++ b/web/projects/ui/src/app/pages/marketplace-routes/marketplace-show/marketplace-show-controls/marketplace-show-controls.component.ts @@ -4,28 +4,29 @@ import { Inject, Input, } from '@angular/core' -import { AlertController, LoadingController } from '@ionic/angular' +import { AlertController } from '@ionic/angular' import { AbstractMarketplaceService, MarketplacePkg, } from '@start9labs/marketplace' import { Emver, - ErrorToastService, + ErrorService, isEmptyObject, + LoadingService, sameUrl, } from '@start9labs/shared' +import { PatchDB } from 'patch-db-client' +import { firstValueFrom } from 'rxjs' +import { ClientStorageService } from 'src/app/services/client-storage.service' +import { MarketplaceService } from 'src/app/services/marketplace.service' import { DataModel, PackageDataEntry, } from 'src/app/services/patch-db/data-model' -import { ClientStorageService } from 'src/app/services/client-storage.service' -import { MarketplaceService } from 'src/app/services/marketplace.service' -import { hasCurrentDeps } from 'src/app/util/has-deps' -import { PatchDB } from 'patch-db-client' -import { getAllPackages, getManifest } from 'src/app/util/get-package-data' -import { firstValueFrom } from 'rxjs' import { dryUpdate } from 'src/app/util/dry-update' +import { getAllPackages, getManifest } from 'src/app/util/get-package-data' +import { hasCurrentDeps } from 'src/app/util/has-deps' @Component({ selector: 'marketplace-show-controls', @@ -50,9 +51,9 @@ export class MarketplaceShowControlsComponent { private readonly ClientStorageService: ClientStorageService, @Inject(AbstractMarketplaceService) private readonly marketplaceService: MarketplaceService, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly emver: Emver, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly patch: PatchDB, ) {} @@ -176,19 +177,16 @@ export class MarketplaceShowControlsComponent { } private async install(url: string) { - const loader = await this.loadingCtrl.create({ - message: 'Beginning Install...', - }) - await loader.present() + const loader = this.loader.open('Beginning Install...').subscribe() const { id, version } = this.pkg.manifest try { await this.marketplaceService.installPackage(id, version, url) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/pages/notifications/notifications.page.ts b/web/projects/ui/src/app/pages/notifications/notifications.page.ts index 933c50e67..69bad6c29 100644 --- a/web/projects/ui/src/app/pages/notifications/notifications.page.ts +++ b/web/projects/ui/src/app/pages/notifications/notifications.page.ts @@ -1,21 +1,17 @@ import { Component } from '@angular/core' -import { ApiService } from 'src/app/services/api/embassy-api.service' +import { ActivatedRoute } from '@angular/router' +import { AlertController, ModalController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { PatchDB } from 'patch-db-client' +import { first } from 'rxjs' +import { BackupReportPage } from 'src/app/modals/backup-report/backup-report.page' import { - ServerNotifications, NotificationLevel, ServerNotification, + ServerNotifications, } from 'src/app/services/api/api.types' -import { - AlertController, - LoadingController, - ModalController, -} from '@ionic/angular' -import { ActivatedRoute } from '@angular/router' -import { ErrorToastService } from '@start9labs/shared' -import { BackupReportPage } from 'src/app/modals/backup-report/backup-report.page' -import { PatchDB } from 'patch-db-client' +import { ApiService } from 'src/app/services/api/embassy-api.service' import { DataModel } from 'src/app/services/patch-db/data-model' -import { first } from 'rxjs' @Component({ selector: 'notifications', @@ -34,9 +30,9 @@ export class NotificationsPage { constructor( private readonly embassyApi: ApiService, private readonly alertCtrl: AlertController, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly modalCtrl: ModalController, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly route: ActivatedRoute, private readonly patch: PatchDB, ) {} @@ -66,26 +62,23 @@ export class NotificationsPage { return notifications } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } return [] } async delete(id: number, index: number): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Deleting...', - }) - await loader.present() + const loader = this.loader.open('Deleting...').subscribe() try { await this.embassyApi.deleteNotification({ id }) this.notifications.splice(index, 1) this.beforeCursor = this.notifications[this.notifications.length - 1]?.id } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -160,10 +153,7 @@ export class NotificationsPage { } private async deleteAll(): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Deleting...', - }) - await loader.present() + const loader = this.loader.open('Deleting...').subscribe() try { await this.embassyApi.deleteAllNotifications({ @@ -172,9 +162,9 @@ export class NotificationsPage { this.notifications = [] this.beforeCursor = undefined } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/pages/server-routes/restore/restore.component.ts b/web/projects/ui/src/app/pages/server-routes/restore/restore.component.ts index 88e9cb4a8..ae644399f 100644 --- a/web/projects/ui/src/app/pages/server-routes/restore/restore.component.ts +++ b/web/projects/ui/src/app/pages/server-routes/restore/restore.component.ts @@ -1,14 +1,10 @@ import { Component } from '@angular/core' -import { - LoadingController, - ModalController, - NavController, -} from '@ionic/angular' +import { ModalController, NavController } from '@ionic/angular' +import { LoadingService } from '@start9labs/shared' +import { TuiDialogService } from '@taiga-ui/core' +import { take } from 'rxjs/operators' +import { PROMPT, PromptOptions } from 'src/app/modals/prompt.component' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { - GenericInputComponent, - GenericInputOptions, -} from 'src/app/modals/generic-input/generic-input.component' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import { BackupInfo, @@ -26,37 +22,35 @@ import * as argon2 from '@start9labs/argon2' export class RestorePage { constructor( private readonly modalCtrl: ModalController, + private readonly dialogs: TuiDialogService, private readonly navCtrl: NavController, private readonly embassyApi: ApiService, - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, ) {} async presentModalPassword( target: MappedBackupTarget, ): Promise { - const options: GenericInputOptions = { - title: 'Password Required', + const options: PromptOptions = { message: 'Enter the master password that was used to encrypt this backup. On the next screen, you will select the individual services you want to restore.', label: 'Master Password', placeholder: 'Enter master password', useMask: true, buttonText: 'Next', - submitFn: async (password: string) => { + } + + this.dialogs + .open(PROMPT, { + label: 'Password Required', + data: options, + }) + .pipe(take(1)) + .subscribe(async (password: string) => { const passwordHash = target.entry.startOs?.passwordHash || '' argon2.verify(passwordHash, password) await this.restoreFromBackup(target, password) - }, - } - - const modal = await this.modalCtrl.create({ - componentProps: { options }, - cssClass: 'alertlike-modal', - presentingElement: await this.modalCtrl.getTop(), - component: GenericInputComponent, - }) - - await modal.present() + }) } private async restoreFromBackup( @@ -64,10 +58,7 @@ export class RestorePage { password: string, oldPassword?: string, ): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Decrypting drive...', - }) - await loader.present() + const loader = this.loader.open('Decrypting drive...').subscribe() try { const backupInfo = await this.embassyApi.getBackupInfo({ @@ -76,7 +67,7 @@ export class RestorePage { }) this.presentModalSelect(target.id, backupInfo, password, oldPassword) } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts b/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts index 0467df102..804a88fab 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-backup/server-backup.page.ts @@ -1,16 +1,11 @@ import { Component } from '@angular/core' -import { - LoadingController, - ModalController, - NavController, -} from '@ionic/angular' +import { ModalController, NavController } from '@ionic/angular' +import { LoadingService } from '@start9labs/shared' +import { TuiDialogService } from '@taiga-ui/core' +import { PROMPT, PromptOptions } from 'src/app/modals/prompt.component' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { - GenericInputComponent, - GenericInputOptions, -} from 'src/app/modals/generic-input/generic-input.component' import { PatchDB } from 'patch-db-client' -import { skip, takeUntil } from 'rxjs/operators' +import { skip, take, takeUntil } from 'rxjs/operators' import { MappedBackupTarget } from 'src/app/types/mapped-backup-target' import * as argon2 from '@start9labs/argon2' import { TuiDestroyService } from '@taiga-ui/cdk' @@ -35,7 +30,8 @@ export class ServerBackupPage { readonly backingUp$ = this.eosService.backingUp$ constructor( - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, + private readonly dialogs: TuiDialogService, private readonly modalCtrl: ModalController, private readonly embassyApi: ApiService, private readonly navCtrl: NavController, @@ -75,14 +71,21 @@ export class ServerBackupPage { private async presentModalPassword( target: MappedBackupTarget, ): Promise { - const options: GenericInputOptions = { - title: 'Master Password Needed', + const options: PromptOptions = { message: 'Enter your master password to encrypt this backup.', label: 'Master Password', placeholder: 'Enter master password', useMask: true, buttonText: 'Create Backup', - submitFn: async (password: string) => { + } + + this.dialogs + .open(PROMPT, { + label: 'Master Password Needed', + data: options, + }) + .pipe(take(1)) + .subscribe(async (password: string) => { // confirm password matches current master password const { passwordHash } = await getServerInfo(this.patch) argon2.verify(passwordHash, password) @@ -105,45 +108,34 @@ export class ServerBackupPage { } await this.createBackup(target, password) } - }, - } - - const m = await this.modalCtrl.create({ - component: GenericInputComponent, - componentProps: { options }, - cssClass: 'alertlike-modal', - }) - - await m.present() + }) } private async presentModalOldPassword( target: MappedBackupTarget, password: string, ): Promise { - const options: GenericInputOptions = { - title: 'Original Password Needed', + const options: PromptOptions = { message: 'This backup was created with a different password. Enter the ORIGINAL password that was used to encrypt this backup.', label: 'Original Password', placeholder: 'Enter original password', useMask: true, buttonText: 'Create Backup', - submitFn: async (oldPassword: string) => { + } + + this.dialogs + .open(PROMPT, { + label: 'Original Password Needed', + data: options, + }) + .pipe(take(1)) + .subscribe(async (oldPassword: string) => { const passwordHash = target.entry.startOs?.passwordHash || '' argon2.verify(passwordHash, oldPassword) await this.createBackup(target, password, oldPassword) - }, - } - - const m = await this.modalCtrl.create({ - component: GenericInputComponent, - componentProps: { options }, - cssClass: 'alertlike-modal', - }) - - await m.present() + }) } private async createBackup( @@ -151,10 +143,7 @@ export class ServerBackupPage { password: string, oldPassword?: string, ): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Beginning backup...', - }) - await loader.present() + const loader = this.loader.open('Beginning backup...').subscribe() try { await this.embassyApi.createBackup({ @@ -164,7 +153,7 @@ export class ServerBackupPage { password, }) } finally { - loader.dismiss() + loader.unsubscribe() } } } diff --git a/web/projects/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts b/web/projects/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts index 569d34a45..4678fe3ea 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-metrics/server-metrics.page.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core' +import { ErrorService, pauseFor } from '@start9labs/shared' +import { Subject } from 'rxjs' import { Metrics } from 'src/app/services/api/api.types' import { ApiService } from 'src/app/services/api/embassy-api.service' import { TimeService } from 'src/app/services/time-service' -import { pauseFor, ErrorToastService } from '@start9labs/shared' -import { Subject } from 'rxjs' @Component({ selector: 'server-metrics', @@ -18,7 +18,7 @@ export class ServerMetricsPage { readonly uptime$ = this.timeService.uptime$ constructor( - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, private readonly timeService: TimeService, ) {} @@ -50,7 +50,7 @@ export class ServerMetricsPage { const metrics = await this.embassyApi.getServerMetrics({}) this.metrics$.next(metrics) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) this.stopDaemon() } } diff --git a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts index f375b0e86..5ade9c666 100644 --- a/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -1,32 +1,32 @@ import { Component, Inject } from '@angular/core' +import { ActivatedRoute } from '@angular/router' import { AlertController, - LoadingController, ModalController, NavController, ToastController, } from '@ionic/angular' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ActivatedRoute } from '@angular/router' -import { PatchDB } from 'patch-db-client' -import { firstValueFrom, Observable, of } from 'rxjs' -import { ErrorToastService } from '@start9labs/shared' -import { EOSService } from 'src/app/services/eos.service' -import { ClientStorageService } from 'src/app/services/client-storage.service' -import { OSUpdatePage } from 'src/app/modals/os-update/os-update.page' -import { getAllPackages } from '../../../util/get-package-data' -import { AuthService } from 'src/app/services/auth.service' -import { DataModel } from 'src/app/services/patch-db/data-model' -import { - GenericInputComponent, - GenericInputOptions, -} from 'src/app/modals/generic-input/generic-input.component' -import { ConfigService } from 'src/app/services/config.service' import { WINDOW } from '@ng-web-apis/common' -import { getServerInfo } from 'src/app/util/get-server-info' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' -import { ConfigSpec } from 'src/app/pkg-config/config-types' import * as argon2 from '@start9labs/argon2' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { CB } from '@start9labs/start-sdk' +import { TuiAlertService, TuiDialogService } from '@taiga-ui/core' +import { TUI_PROMPT } from '@taiga-ui/kit' +import { PatchDB } from 'patch-db-client' +import { filter, from, Observable, of, switchMap } from 'rxjs' +import { take } from 'rxjs/operators' +import { FormComponent } from 'src/app/components/form.component' +import { OSUpdatePage } from 'src/app/modals/os-update/os-update.page' +import { PROMPT } from 'src/app/modals/prompt.component' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { AuthService } from 'src/app/services/auth.service' +import { ClientStorageService } from 'src/app/services/client-storage.service' +import { ConfigService } from 'src/app/services/config.service' +import { EOSService } from 'src/app/services/eos.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' +import { getServerInfo } from 'src/app/util/get-server-info' @Component({ selector: 'server-show', @@ -45,8 +45,11 @@ export class ServerShowPage { constructor( private readonly alertCtrl: AlertController, private readonly modalCtrl: ModalController, - private readonly loadingCtrl: LoadingController, - private readonly errToast: ErrorToastService, + private readonly alerts: TuiAlertService, + private readonly dialogs: TuiDialogService, + private readonly formDialog: FormDialogService, + private readonly loader: LoadingService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, private readonly navCtrl: NavController, private readonly route: ActivatedRoute, @@ -60,123 +63,110 @@ export class ServerShowPage { ) {} async setBrowserTab(): Promise { - const chosenName = await firstValueFrom(this.patch.watch$('ui', 'name')) + this.patch + .watch$('ui', 'name') + .pipe( + switchMap(initialValue => + this.dialogs.open(PROMPT, { + label: 'Browser Tab Title', + data: { + message: `This value will be displayed as the title of your browser tab.`, + label: 'Device Name', + placeholder: 'StartOS', + required: false, + buttonText: 'Save', + initialValue, + }, + }), + ), + take(1), + ) + .subscribe(async name => { + const loader = this.loader.open('Saving...').subscribe() - const options: GenericInputOptions = { - title: 'Browser Tab Title', - message: `This value will be displayed as the title of your browser tab.`, - label: 'Device Name', - useMask: false, - placeholder: 'StartOS', - nullable: true, - initialValue: chosenName, - buttonText: 'Save', - submitFn: (name: string) => this.setName(name || null), - } - - const modal = await this.modalCtrl.create({ - componentProps: { options }, - cssClass: 'alertlike-modal', - presentingElement: await this.modalCtrl.getTop(), - component: GenericInputComponent, - }) - - await modal.present() + try { + await this.embassyApi.setDbValue( + ['name'], + name || null, + ) + } finally { + loader.unsubscribe() + } + }) } async presentAlertResetPassword() { - const alert = await this.alertCtrl.create({ - header: 'Warning', - message: - 'You will still need your current password to decrypt existing backups!', - buttons: [ - { - text: 'Cancel', - role: 'cancel', + this.dialogs + .open(TUI_PROMPT, { + label: 'Warning', + size: 's', + data: { + content: + 'You will still need your current password to decrypt existing backups!', + yes: 'Continue', + no: 'Cancel', }, - { - text: 'Continue', - handler: () => this.presentModalResetPassword(), - cssClass: 'enter-click', - }, - ], - cssClass: 'alert-warning-message', - }) - - await alert.present() - } - - async presentModalResetPassword(): Promise { - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: 'Change Master Password', - spec: PasswordSpec, - buttons: [ - { - text: 'Save', - handler: (value: any) => { - return this.resetPassword(value) - }, - isSubmit: true, + }) + .pipe( + filter(Boolean), + switchMap(() => from(configBuilderToSpec(passwordSpec))), + ) + .subscribe(spec => { + this.formDialog.open(FormComponent, { + label: 'Change Master Password', + data: { + spec, + buttons: [ + { + text: 'Save', + handler: (value: PasswordSpec) => this.resetPassword(value), + }, + ], }, - ], - }, - }) - await modal.present() + }) + }) } - private async resetPassword(value: { - currPass: string - newPass: string - newPass2: string - }): Promise { + private async resetPassword(value: PasswordSpec): Promise { let err = '' - if (value.newPass !== value.newPass2) { + if (value.newPassword1 !== value.newPassword2) { err = 'New passwords do not match' - } else if (value.newPass.length < 12) { + } else if (value.newPassword1.length < 12) { err = 'New password must be 12 characters or greater' - } else if (value.newPass.length > 64) { + } else if (value.newPassword1.length > 64) { err = 'New password must be less than 65 characters' } // confirm current password is correct const { passwordHash } = await getServerInfo(this.patch) try { - argon2.verify(passwordHash, value.currPass) + argon2.verify(passwordHash, value.currentPassword) } catch (e) { err = 'Current password is invalid' } if (err) { - this.errToast.present(err) + this.errorService.handleError(err) return false } - const loader = await this.loadingCtrl.create({ - message: 'Changing master password...', - }) - await loader.present() + const loader = this.loader.open('Saving...').subscribe() try { await this.embassyApi.resetPassword({ - oldPassword: value.currPass, - newPassword: value.newPass, - }) - const toast = await this.toastCtrl.create({ - header: 'Password changed!', - position: 'bottom', - duration: 2000, + oldPassword: value.currentPassword, + newPassword: value.newPassword1, }) - toast.present() + this.alerts.open('Password changed!').subscribe() + return true } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) return false } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -187,7 +177,7 @@ export class ServerShowPage { const alert = await this.alertCtrl.create({ header: isTor ? 'Warning' : 'Confirm', message: isTor - ? `You are currently connected over Tor. If you reset the Tor daemon, you will loose connectivity until it comes back online.

${shared}` + ? `You are currently connected over Tor. If you reset the Tor daemon, you will lose connectivity until it comes back online.

${shared}` : `Reset Tor?

${shared}`, inputs: [ { @@ -215,10 +205,7 @@ export class ServerShowPage { } private async resetTor(wipeState: boolean) { - const loader = await this.loadingCtrl.create({ - message: 'Resetting Tor...', - }) - await loader.present() + const loader = this.loader.open('Resetting Tor...').subscribe() try { await this.embassyApi.resetTor({ @@ -241,9 +228,9 @@ export class ServerShowPage { }) await toast.present() } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -336,7 +323,7 @@ export class ServerShowPage { this.restart() }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } }, cssClass: 'enter-click', @@ -360,19 +347,6 @@ export class ServerShowPage { } } - private async setName(value: string | null): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Saving...', - }) - await loader.present() - - try { - await this.embassyApi.setDbValue(['name'], value) - } finally { - loader.dismiss() - } - } - // should wipe cache independent of actual BE logout private logout() { this.embassyApi.logout({}).catch(e => console.error('Failed to log out', e)) @@ -381,48 +355,37 @@ export class ServerShowPage { private async restart() { const action = 'Restart' - - const loader = await this.loadingCtrl.create({ - message: `Beginning ${action}...`, - }) - await loader.present() + const loader = this.loader.open(`Beginning ${action}...`).subscribe() try { await this.embassyApi.restartServer({}) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async shutdown() { const action = 'Shutdown' - - const loader = await this.loadingCtrl.create({ - message: `Beginning ${action}...`, - }) - await loader.present() + const loader = this.loader.open(`Beginning ${action}...`).subscribe() try { await this.embassyApi.shutdownServer({}) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async checkForEosUpdate(): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Checking for updates', - }) - await loader.present() + const loader = this.loader.open('Checking for updates').subscribe() try { await this.eosService.loadEos() - await loader.dismiss() + await loader.unsubscribe() if (this.eosService.updateAvailable$.value) { this.updateEos() @@ -430,8 +393,8 @@ export class ServerShowPage { this.presentAlertLatest() } } catch (e: any) { - await loader.dismiss() - this.errToast.present(e) + await loader.unsubscribe() + this.errorService.handleError(e) } } @@ -729,29 +692,28 @@ interface SettingBtn { disabled$: Observable } -const PasswordSpec: ConfigSpec = { - currPass: { - type: 'string', +const passwordSpec = CB.Config.of({ + currentPassword: CB.Value.text({ name: 'Current Password', - placeholder: 'CurrentPass', - nullable: false, + required: { + default: null, + }, masked: true, - copyable: false, - }, - newPass: { - type: 'string', + }), + newPassword1: CB.Value.text({ name: 'New Password', - placeholder: 'NewPass', - nullable: false, + required: { + default: null, + }, masked: true, - copyable: false, - }, - newPass2: { - type: 'string', + }), + newPassword2: CB.Value.text({ name: 'Retype New Password', - placeholder: 'NewPass', - nullable: false, + required: { + default: null, + }, masked: true, - copyable: false, - }, -} + }), +}) + +export type PasswordSpec = typeof passwordSpec.validator._TYPE diff --git a/web/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts b/web/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts index d6a1a42e5..9dfdccb66 100644 --- a/web/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/sessions/sessions.page.ts @@ -1,8 +1,8 @@ import { Component } from '@angular/core' -import { AlertController, LoadingController } from '@ionic/angular' -import { ErrorToastService } from '@start9labs/shared' -import { ApiService } from 'src/app/services/api/embassy-api.service' +import { AlertController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' import { PlatformType, Session } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' @Component({ selector: 'sessions', @@ -15,8 +15,8 @@ export class SessionsPage { otherSessions: SessionWithId[] = [] constructor( - private readonly loadingCtrl: LoadingController, - private readonly errToast: ErrorToastService, + private readonly loader: LoadingService, + private readonly errorService: ErrorService, private readonly alertCtrl: AlertController, private readonly embassyApi: ApiService, ) {} @@ -39,7 +39,7 @@ export class SessionsPage { ) }) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { this.loading = false } @@ -67,18 +67,17 @@ export class SessionsPage { } async kill(ids: string[]): Promise { - const loader = await this.loadingCtrl.create({ - message: `Terminating session${ids.length > 1 ? 's' : ''}...`, - }) - await loader.present() + const loader = this.loader + .open(`Terminating session${ids.length > 1 ? 's' : ''}...`) + .subscribe() try { await this.embassyApi.killSessions({ ids }) this.otherSessions = this.otherSessions.filter(s => !ids.includes(s.id)) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } diff --git a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts index f6410e748..a0da8609d 100644 --- a/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/sideload/sideload.page.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core' -import { isPlatform, LoadingController, NavController } from '@ionic/angular' +import { isPlatform, NavController } from '@ionic/angular' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { S9pk, T } from '@start9labs/start-sdk' +import cbor from 'cbor' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ConfigService } from 'src/app/services/config.service' -import cbor from 'cbor' -import { ErrorToastService } from '@start9labs/shared' -import { S9pk, T } from '@start9labs/start-sdk' interface Positions { [key: string]: [bigint, bigint] // [position, length] @@ -37,10 +37,10 @@ export class SideloadPage { } constructor( - private readonly loadingCtrl: LoadingController, + private readonly loader: LoadingService, private readonly api: ApiService, private readonly navCtrl: NavController, - private readonly errToast: ErrorToastService, + private readonly errorService: ErrorService, private readonly config: ConfigService, ) {} @@ -111,11 +111,8 @@ export class SideloadPage { } async handleUpload() { - const loader = await this.loadingCtrl.create({ - message: 'Uploading package', - cssClass: 'loader', - }) - await loader.present() + const loader = this.loader.open('Uploading package').subscribe() + try { const res = await this.api.sideloadPackage() this.api @@ -124,9 +121,9 @@ export class SideloadPage { this.navCtrl.navigateRoot('/services') } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() this.clearToUpload() } } diff --git a/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.html b/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.html index 61737639b..4dd44dfd1 100644 --- a/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.html +++ b/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.html @@ -24,7 +24,7 @@ Saved Keys - + Add New Key @@ -62,7 +62,7 @@ - +

{{ ssh.hostname }}

@@ -73,7 +73,7 @@ slot="end" fill="clear" color="danger" - (click)="presentAlertDelete(i)" + (click)="delete(ssh)" > Remove diff --git a/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts b/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts index 476835566..5e474db0f 100644 --- a/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/ssh-keys/ssh-keys.page.ts @@ -1,16 +1,12 @@ -import { Component } from '@angular/core' -import { - AlertController, - LoadingController, - ModalController, -} from '@ionic/angular' +import { ChangeDetectorRef, Component } from '@angular/core' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit' +import { filter } from 'rxjs' +import { take } from 'rxjs/operators' +import { PROMPT } from 'src/app/modals/prompt.component' import { SSHKey } from 'src/app/services/api/api.types' -import { ErrorToastService } from '@start9labs/shared' import { ApiService } from 'src/app/services/api/embassy-api.service' -import { - GenericInputComponent, - GenericInputOptions, -} from 'src/app/modals/generic-input/generic-input.component' @Component({ selector: 'ssh-keys', @@ -23,10 +19,10 @@ export class SSHKeysPage { readonly docsUrl = 'https://docs.start9.com/0.3.5.x/user-manual/ssh' constructor( - private readonly loadingCtrl: LoadingController, - private readonly modalCtrl: ModalController, - private readonly errToast: ErrorToastService, - private readonly alertCtrl: AlertController, + private readonly cdr: ChangeDetectorRef, + private readonly loader: LoadingService, + private readonly dialogs: TuiDialogService, + private readonly errorService: ErrorService, private readonly embassyApi: ApiService, ) {} @@ -38,89 +34,62 @@ export class SSHKeysPage { try { this.sshKeys = await this.embassyApi.getSshKeys({}) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { this.loading = false } } - async presentModalAdd() { - const { name, description } = sshSpec + add() { + this.dialogs + .open(PROMPT, ADD_OPTIONS) + .pipe(take(1)) + .subscribe(async key => { + const loader = this.loader.open('Saving...').subscribe() - const options: GenericInputOptions = { - title: name, - message: description, - label: name, - submitFn: (pk: string) => this.add(pk), - } - - const modal = await this.modalCtrl.create({ - component: GenericInputComponent, - componentProps: { options }, - cssClass: 'alertlike-modal', - }) - await modal.present() + try { + this.sshKeys?.push(await this.embassyApi.addSshKey({ key })) + } finally { + loader.unsubscribe() + this.cdr.markForCheck() + } + }) } - async add(pubkey: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Saving...', - }) - await loader.present() + delete(key: SSHKey) { + this.dialogs + .open(TUI_PROMPT, DELETE_OPTIONS) + .pipe(filter(Boolean)) + .subscribe(async () => { + const loader = this.loader.open('Deleting...').subscribe() - try { - const key = await this.embassyApi.addSshKey({ key: pubkey }) - this.sshKeys.push(key) - } finally { - loader.dismiss() - } - } - - async presentAlertDelete(i: number) { - const alert = await this.alertCtrl.create({ - header: 'Caution', - message: `Are you sure you want to delete this key?`, - buttons: [ - { - text: 'Cancel', - role: 'cancel', - }, - { - text: 'Delete', - handler: () => { - this.delete(i) - }, - cssClass: 'enter-click', - }, - ], - }) - await alert.present() - } - - async delete(i: number): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Deleting...', - }) - await loader.present() - - try { - const entry = this.sshKeys[i] - await this.embassyApi.deleteSshKey({ fingerprint: entry.fingerprint }) - this.sshKeys.splice(i, 1) - } catch (e: any) { - this.errToast.present(e) - } finally { - loader.dismiss() - } + try { + await this.embassyApi.deleteSshKey({ fingerprint: key.fingerprint }) + this.sshKeys?.splice(this.sshKeys?.indexOf(key), 1) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + this.cdr.markForCheck() + } + }) } } -const sshSpec = { - type: 'string', - name: 'SSH Key', - description: - 'Enter the SSH public key you would like to authorize for root access to your server.', - nullable: false, - masked: false, - copyable: false, +const ADD_OPTIONS: Partial> = { + label: 'SSH Key', + data: { + message: + 'Enter the SSH public key you would like to authorize for root access to your Embassy.', + }, +} + +const DELETE_OPTIONS: Partial> = { + label: 'Confirm', + size: 's', + data: { + content: 'Delete key? This action cannot be undone.', + yes: 'Delete', + no: 'Cancel', + }, } diff --git a/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts index e6d5da252..0a1f6a7d9 100644 --- a/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/web/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -2,19 +2,23 @@ import { Component, Inject } from '@angular/core' import { ActionSheetController, AlertController, - LoadingController, - ModalController, ToastController, } from '@ionic/angular' -import { AlertInput } from '@ionic/core' -import { ApiService } from 'src/app/services/api/embassy-api.service' -import { ActionSheetButton } from '@ionic/core' -import { ValueSpecObject } from 'src/app/pkg-config/config-types' -import { RR } from 'src/app/services/api/api.types' -import { pauseFor, ErrorToastService } from '@start9labs/shared' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' -import { ConfigService } from 'src/app/services/config.service' +import { ActionSheetButton, AlertInput } from '@ionic/core' import { WINDOW } from '@ng-web-apis/common' +import { ErrorService, LoadingService, pauseFor } from '@start9labs/shared' +import { CT } from '@start9labs/start-sdk' +import { TuiDialogOptions } from '@taiga-ui/core' +import { FormComponent, FormContext } from 'src/app/components/form.component' +import { RR } from 'src/app/services/api/api.types' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { ConfigService } from 'src/app/services/config.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' + +export interface WiFiForm { + ssid: string + password: string +} @Component({ selector: 'wifi', @@ -32,9 +36,9 @@ export class WifiPage { private readonly api: ApiService, private readonly toastCtrl: ToastController, private readonly alertCtrl: AlertController, - private readonly loadingCtrl: LoadingController, - private readonly modalCtrl: ModalController, - private readonly errToast: ErrorToastService, + private readonly loader: LoadingService, + private readonly formDialog: FormDialogService, + private readonly errorService: ErrorService, private readonly actionCtrl: ActionSheetController, private readonly config: ConfigService, @Inject(WINDOW) private readonly windowRef: Window, @@ -60,7 +64,7 @@ export class WifiPage { await this.presentAlertCountry() } } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { this.loading = false } @@ -120,30 +124,25 @@ export class WifiPage { async presentModalAdd(ssid?: string, needsPW: boolean = true) { const wifiSpec = getWifiValueSpec(ssid, needsPW) - const modal = await this.modalCtrl.create({ - component: GenericFormPage, - componentProps: { - title: wifiSpec.name, + const options: Partial>> = { + label: wifiSpec.name, + data: { spec: wifiSpec.spec, buttons: [ { text: 'Save for Later', - handler: async (value: { ssid: string; password: string }) => { - await this.save(value.ssid, value.password) - }, + handler: async ({ ssid, password }) => this.save(ssid, password), }, { text: 'Save and Connect', - handler: async (value: { ssid: string; password: string }) => { - await this.saveAndConnect(value.ssid, value.password) - }, - isSubmit: true, + handler: async ({ ssid, password }) => + this.saveAndConnect(ssid, password), }, ], }, - }) + } - await modal.present() + this.formDialog.open(FormComponent, options) } async presentAction(ssid: string) { @@ -179,19 +178,16 @@ export class WifiPage { } private async setCountry(country: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Setting country...', - }) - await loader.present() + const loader = this.loader.open('Setting country...').subscribe() try { await this.api.setWifiCountry({ country }) await this.getWifi() this.wifi.country = country } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } @@ -270,43 +266,36 @@ export class WifiPage { } private async connect(ssid: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Connecting. This could take a while...', - }) - await loader.present() + const loader = this.loader + .open('Connecting. This could take a while...') + .subscribe() try { await this.api.connectWifi({ ssid }) await this.confirmWifi(ssid) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async delete(ssid: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Deleting...', - }) - await loader.present() + const loader = this.loader.open('Deleting...').subscribe() try { await this.api.deleteWifi({ ssid }) await this.getWifi() delete this.wifi.ssids[ssid] } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async save(ssid: string, password: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Saving...', - }) - await loader.present() + const loader = this.loader.open('Saving...').subscribe() try { await this.api.addWifi({ @@ -317,17 +306,16 @@ export class WifiPage { }) await this.getWifi() } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } private async saveAndConnect(ssid: string, password: string): Promise { - const loader = await this.loadingCtrl.create({ - message: 'Connecting. This could take a while...', - }) - await loader.present() + const loader = this.loader + .open('Connecting. This could take a while...') + .subscribe() try { await this.api.addWifi({ @@ -339,39 +327,62 @@ export class WifiPage { await this.confirmWifi(ssid, true) } catch (e: any) { - this.errToast.present(e) + this.errorService.handleError(e) } finally { - loader.dismiss() + loader.unsubscribe() } } } function getWifiValueSpec( - ssid?: string, + ssid: string | null = null, needsPW: boolean = true, -): ValueSpecObject { +): CT.ValueSpecObject { return { + warning: null, type: 'object', name: 'WiFi Credentials', description: 'Enter the network SSID and password. You can connect now or save the network for later.', spec: { ssid: { - type: 'string', + type: 'text', + minLength: null, + maxLength: null, + patterns: [], name: 'Network SSID', - nullable: false, + description: null, + inputmode: 'text', + placeholder: null, + required: true, masked: false, - copyable: false, default: ssid, + warning: null, + disabled: false, + immutable: false, + generate: null, }, password: { - type: 'string', + type: 'text', + minLength: null, + maxLength: null, + patterns: [ + { + regex: '^.{8,}$', + description: 'Must be longer than 8 characters', + }, + ], name: 'Password', - nullable: !needsPW, + description: null, + inputmode: 'text', + placeholder: null, + required: needsPW, masked: true, - copyable: false, - pattern: '^.{8,}$', - 'pattern-description': 'Must be longer than 8 characters', + default: null, + warning: null, + disabled: false, + immutable: false, + generate: null, }, }, } diff --git a/web/projects/ui/src/app/pkg-config/config-types.ts b/web/projects/ui/src/app/pkg-config/config-types.ts deleted file mode 100644 index f73dbc0a4..000000000 --- a/web/projects/ui/src/app/pkg-config/config-types.ts +++ /dev/null @@ -1,159 +0,0 @@ -export type ConfigSpec = Record - -export type ValueType = - | 'string' - | 'number' - | 'boolean' - | 'enum' - | 'list' - | 'object' - | 'pointer' - | 'union' -export type ValueSpec = ValueSpecOf - -// core spec types. These types provide the metadata for performing validations -export type ValueSpecOf = T extends 'string' - ? ValueSpecString - : T extends 'number' - ? ValueSpecNumber - : T extends 'boolean' - ? ValueSpecBoolean - : T extends 'enum' - ? ValueSpecEnum - : T extends 'list' - ? ValueSpecList - : T extends 'object' - ? ValueSpecObject - : T extends 'union' - ? ValueSpecUnion - : never - -export interface ValueSpecString extends ListValueSpecString, WithStandalone { - type: 'string' - default?: DefaultString - nullable: boolean - textarea?: boolean -} - -export interface ValueSpecNumber extends ListValueSpecNumber, WithStandalone { - type: 'number' - nullable: boolean - default?: number -} - -export interface ValueSpecEnum extends ListValueSpecEnum, WithStandalone { - type: 'enum' - default: string -} - -export interface ValueSpecBoolean extends WithStandalone { - type: 'boolean' - default: boolean -} - -export interface ValueSpecUnion { - type: 'union' - tag: UnionTagSpec - variants: { [key: string]: ConfigSpec } - default: string -} - -export interface ValueSpecObject extends WithStandalone { - type: 'object' - spec: ConfigSpec -} - -export interface WithStandalone { - name: string - description?: string - warning?: string -} - -// no lists of booleans, lists, pointers -export type ListValueSpecType = - | 'string' - | 'number' - | 'enum' - | 'object' - | 'union' - -// represents a spec for the values of a list -export type ListValueSpecOf = T extends 'string' - ? ListValueSpecString - : T extends 'number' - ? ListValueSpecNumber - : T extends 'enum' - ? ListValueSpecEnum - : T extends 'object' - ? ListValueSpecObject - : T extends 'union' - ? ListValueSpecUnion - : never - -// represents a spec for a list -export type ValueSpecList = ValueSpecListOf -export interface ValueSpecListOf - extends WithStandalone { - type: 'list' - subtype: T - spec: ListValueSpecOf - range: string // '[0,1]' (inclusive) OR '[0,*)' (right unbounded), normal math rules - default: string[] | number[] | DefaultString[] | object[] -} - -// sometimes the type checker needs just a little bit of help -export function isValueSpecListOf( - t: ValueSpecList, - s: S, -): t is ValueSpecListOf { - return t.subtype === s -} - -export interface ListValueSpecString { - pattern?: string - 'pattern-description'?: string - masked: boolean - copyable: boolean - placeholder?: string -} - -export interface ListValueSpecNumber { - range: string - integral: boolean - units?: string - placeholder?: string -} - -export interface ListValueSpecEnum { - values: string[] - 'value-names': { [value: string]: string } -} - -export interface ListValueSpecObject { - spec: ConfigSpec // this is a mapped type of the config object at this level, replacing the object's values with specs on those values - 'unique-by': UniqueBy // indicates whether duplicates can be permitted in the list - 'display-as'?: string // this should be a handlebars template which can make use of the entire config which corresponds to 'spec' -} - -export type UniqueBy = null | string | { any: UniqueBy[] } | { all: UniqueBy[] } - -export interface ListValueSpecUnion { - tag: UnionTagSpec - variants: { [key: string]: ConfigSpec } - 'display-as'?: string // this may be a handlebars template which can conditionally (on tag.id) make use of each union's entries, or if left blank will display as tag.id - 'unique-by': UniqueBy - default: string // this should be the variantName which one prefers a user to start with by default when creating a new union instance in a list -} - -export interface UnionTagSpec { - id: string // The name of the field containing one of the union variants - 'variant-names': { - // the name of each variant - [variant: string]: string - } - name: string - description?: string - warning?: string -} - -export type DefaultString = string | { charset: string; len: number } diff --git a/web/projects/ui/src/app/pkg-config/config-utilities.ts b/web/projects/ui/src/app/pkg-config/config-utilities.ts deleted file mode 100644 index 431bc1c5d..000000000 --- a/web/projects/ui/src/app/pkg-config/config-utilities.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { ValueSpec, DefaultString } from './config-types' - -export class Range { - min?: number - max?: number - minInclusive!: boolean - maxInclusive!: boolean - - static from(s: string): Range { - const r = new Range() - r.minInclusive = s.startsWith('[') - r.maxInclusive = s.endsWith(']') - const [minStr, maxStr] = s.split(',').map(a => a.trim()) - r.min = minStr === '(*' ? undefined : Number(minStr.slice(1)) - r.max = maxStr === '*)' ? undefined : Number(maxStr.slice(0, -1)) - return r - } - - checkIncludes(n: number) { - if ( - this.hasMin() && - (this.min > n || (!this.minInclusive && this.min == n)) - ) { - throw new Error(this.minMessage()) - } - if ( - this.hasMax() && - (this.max < n || (!this.maxInclusive && this.max == n)) - ) { - throw new Error(this.maxMessage()) - } - } - - hasMin(): this is Range & { min: number } { - return this.min !== undefined - } - - hasMax(): this is Range & { max: number } { - return this.max !== undefined - } - - minMessage(): string { - return `greater than${this.minInclusive ? ' or equal to' : ''} ${this.min}` - } - - maxMessage(): string { - return `less than${this.maxInclusive ? ' or equal to' : ''} ${this.max}` - } - - description(): string { - let message = 'Value can be any number.' - - if (this.hasMin() || this.hasMax()) { - message = 'Value must be' - } - - if (this.hasMin() && this.hasMax()) { - message = `${message} ${this.minMessage()} AND ${this.maxMessage()}.` - } else if (this.hasMin() && !this.hasMax()) { - message = `${message} ${this.minMessage()}.` - } else if (!this.hasMin() && this.hasMax()) { - message = `${message} ${this.maxMessage()}.` - } - - return message - } - - integralMin(): number | undefined { - if (this.min) { - const ceil = Math.ceil(this.min) - if (this.minInclusive) { - return ceil - } else { - if (ceil === this.min) { - return ceil + 1 - } else { - return ceil - } - } - } - } - - integralMax(): number | undefined { - if (this.max) { - const floor = Math.floor(this.max) - if (this.maxInclusive) { - return floor - } else { - if (floor === this.max) { - return floor - 1 - } else { - return floor - } - } - } - } -} - -export function getDefaultDescription(spec: ValueSpec): string { - let toReturn: string | undefined - switch (spec.type) { - case 'string': - if (typeof spec.default === 'string') { - toReturn = spec.default - } else if (typeof spec.default === 'object') { - toReturn = 'random' - } - break - case 'number': - if (typeof spec.default === 'number') { - toReturn = String(spec.default) - } - break - case 'boolean': - toReturn = spec.default === true ? 'True' : 'False' - break - case 'enum': - toReturn = spec['value-names'][spec.default] - break - } - - return toReturn || '' -} - -export function getDefaultString(defaultSpec: DefaultString): string { - if (typeof defaultSpec === 'string') { - return defaultSpec - } else { - let s = '' - for (let i = 0; i < defaultSpec.len; i++) { - s = s + getRandomCharInSet(defaultSpec.charset) - } - - return s - } -} - -// a,g,h,A-Z,,,,- -export function getRandomCharInSet(charset: string): string { - const set = stringToCharSet(charset) - let charIdx = Math.floor(Math.random() * set.len) - for (let range of set.ranges) { - if (range.len > charIdx) { - return String.fromCharCode(range.start.charCodeAt(0) + charIdx) - } - charIdx -= range.len - } - throw new Error('unreachable') -} - -function stringToCharSet(charset: string): CharSet { - let set: CharSet = { ranges: [], len: 0 } - let start: string | null = null - let end: string | null = null - let in_range = false - for (let char of charset) { - switch (char) { - case ',': - if (start !== null && end !== null) { - if (start!.charCodeAt(0) > end!.charCodeAt(0)) { - throw new Error('start > end of charset') - } - const len = end.charCodeAt(0) - start.charCodeAt(0) + 1 - set.ranges.push({ - start, - end, - len, - }) - set.len += len - start = null - end = null - in_range = false - } else if (start !== null && !in_range) { - set.len += 1 - set.ranges.push({ start, end: start, len: 1 }) - start = null - } else if (start !== null && in_range) { - end = ',' - } else if (start === null && end === null && !in_range) { - start = ',' - } else { - throw new Error('unexpected ","') - } - break - case '-': - if (start === null) { - start = '-' - } else if (!in_range) { - in_range = true - } else if (in_range && end === null) { - end = '-' - } else { - throw new Error('unexpected "-"') - } - break - default: - if (start === null) { - start = char - } else if (in_range && end === null) { - end = char - } else { - throw new Error(`unexpected "${char}"`) - } - } - } - if (start !== null && end !== null) { - if (start!.charCodeAt(0) > end!.charCodeAt(0)) { - throw new Error('start > end of charset') - } - const len = end.charCodeAt(0) - start.charCodeAt(0) + 1 - set.ranges.push({ - start, - end, - len, - }) - set.len += len - } else if (start !== null) { - set.len += 1 - set.ranges.push({ - start, - end: start, - len: 1, - }) - } - return set -} - -interface CharSet { - ranges: { - start: string - end: string - len: number - }[] - len: number -} diff --git a/web/projects/ui/src/app/services/api/api.fixures.ts b/web/projects/ui/src/app/services/api/api.fixures.ts index acebdfdfb..e10c682c3 100644 --- a/web/projects/ui/src/app/services/api/api.fixures.ts +++ b/web/projects/ui/src/app/services/api/api.fixures.ts @@ -6,7 +6,8 @@ import { Metric, NotificationLevel, RR, ServerNotifications } from './api.types' import { BTC_ICON, LND_ICON, PROXY_ICON } from './api-icons' import { DependencyMetadata, MarketplacePkg } from '@start9labs/marketplace' import { Log } from '@start9labs/shared' -import { T } from '@start9labs/start-sdk' +import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' +import { T, CB } from '@start9labs/start-sdk' export module Mock { export const ServerUpdated: T.ServerStatus = { @@ -716,651 +717,512 @@ export module Mock { }, } - export const ConfigSpec: RR.GetPackageConfigRes['spec'] = { - bitcoin: { - type: 'object', - name: 'Bitcoin Settings', - description: - 'RPC and P2P interface configuration options for Bitcoin Core', - spec: { - 'bitcoind-p2p': { - type: 'union', - tag: { - id: 'type', - name: 'Bitcoin Core P2P', + export const getInputSpec = async (): Promise< + RR.GetPackageConfigRes['spec'] + > => + configBuilderToSpec( + CB.Config.of({ + bitcoin: CB.Value.object( + { + name: 'Bitcoin Settings', description: - '

The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:

  • Bitcoin Core: The Bitcoin Core service installed on this device
  • External Node: A Bitcoin node running on a different device
', - 'variant-names': { - internal: 'Bitcoin Core', - external: 'External Node', - }, + 'RPC and P2P interface configuration options for Bitcoin Core', }, - default: 'internal', - variants: { - internal: {}, - external: { - 'p2p-host': { - type: 'string', - name: 'Public Address', - description: 'The public address of your Bitcoin Core server', - nullable: false, - masked: false, - copyable: false, - }, - 'p2p-port': { - type: 'number', - name: 'P2P Port', + CB.Config.of({ + 'bitcoind-p2p': CB.Value.union( + { + name: 'P2P Settings', description: - 'The port that your Bitcoin Core P2P server is bound to', - nullable: false, - range: '[0,65535]', - integral: true, - default: 8333, + '

The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:

  • Bitcoin Core: The Bitcoin Core service installed on this device
  • External Node: A Bitcoin node running on a different device
', + required: { default: 'internal' }, }, - }, - }, - }, - }, - }, - advanced: { - name: 'Advanced', - type: 'object', - description: 'Advanced settings', - spec: { - rpcsettings: { - name: 'RPC Settings', - type: 'object', - description: 'rpc username and password', - warning: - 'Adding RPC users gives them special permissions on your node.', - spec: { - rpcuser2: { - name: 'RPC Username', - type: 'string', - description: 'rpc username', - nullable: false, - default: 'defaultrpcusername', - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'must contain only letters.', - masked: false, - copyable: true, - }, - rpcuser: { - name: 'RPC Username', - type: 'string', - description: 'rpc username', - nullable: false, - default: 'defaultrpcusername', - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'must contain only letters.', - masked: false, - copyable: true, - }, - rpcpass: { - name: 'RPC User Password', - type: 'string', - description: 'rpc password', - nullable: false, - default: { - charset: 'a-z,A-Z,2-9', - len: 20, - }, - masked: true, - copyable: true, - }, - rpcpass2: { - name: 'RPC User Password', - type: 'string', - description: 'rpc password', - nullable: false, - default: { - charset: 'a-z,A-Z,2-9', - len: 20, - }, - masked: true, - copyable: true, - }, - }, - }, - }, - }, - testnet: { - name: 'Testnet', - type: 'boolean', - description: - '
  • determines whether your node is running on testnet or mainnet
', - warning: 'Chain will have to resync!', - default: true, - }, - 'object-list': { - name: 'Object List', - type: 'list', - subtype: 'object', - description: 'This is a list of objects, like users or something', - range: '[0,4]', - default: [ - { - 'first-name': 'Admin', - 'last-name': 'User', - age: 40, - }, - { - 'first-name': 'Admin2', - 'last-name': 'User', - age: 40, - }, - ], - // the outer spec here, at the list level, says that what's inside (the inner spec) pertains to its inner elements. - // it just so happens that ValueSpecObject's have the field { spec: ConfigSpec } - // see 'union-list' below for a different example. - spec: { - 'unique-by': 'last-name', - 'display-as': `I'm {{last-name}}, {{first-name}} {{last-name}}`, - spec: { - 'first-name': { - name: 'First Name', - type: 'string', - description: 'User first name', - nullable: true, - masked: false, - copyable: false, - }, - 'last-name': { - name: 'Last Name', - type: 'string', - description: 'User first name', - nullable: true, - default: { - charset: 'a-g,2-9', - len: 12, - }, - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'must contain only letters.', - masked: false, - copyable: true, - }, - age: { - name: 'Age', - type: 'number', - description: 'The age of the user', - nullable: true, - integral: false, - warning: 'User must be at least 18.', - range: '[18,*)', - }, - }, - }, - }, - 'union-list': { - name: 'Union List', - type: 'list', - subtype: 'union', - description: 'This is a sample list of unions', - warning: 'If you change this, things may work.', - // a list of union selections. e.g. 'summer', 'winter',... - default: ['summer'], - range: '[0, 2]', - spec: { - tag: { - id: 'preference', - 'variant-names': { - summer: 'Summer', - winter: 'Winter', - other: 'Other', - }, - name: 'Preference', - }, - // this default is used to make a union selection when a new list element is first created - default: 'summer', - variants: { - summer: { - 'favorite-tree': { - name: 'Favorite Tree', - type: 'string', - nullable: false, - description: 'What is your favorite tree?', - default: 'Maple', - masked: false, - copyable: false, - }, - 'favorite-flower': { - name: 'Favorite Flower', - type: 'enum', - description: 'Select your favorite flower', - 'value-names': { - none: 'Hate Flowers', - red: 'Red', - blue: 'Blue', - purple: 'Purple', - }, - values: ['none', 'red', 'blue', 'purple'], - default: 'none', - }, - }, - winter: { - 'like-snow': { - name: 'Like Snow?', - type: 'boolean', - description: 'Do you like snow or not?', - default: true, - }, - }, - }, - 'unique-by': 'preference', - }, - }, - 'random-enum': { - name: 'Random Enum', - type: 'enum', - 'value-names': { - null: 'Null', - option1: 'One 1', - option2: 'Two 2', - option3: 'Three 3', - }, - default: 'null', - description: 'This is not even real.', - warning: 'Be careful changing this!', - values: ['null', 'option1', 'option2', 'option3'], - }, - 'favorite-number': { - name: 'Favorite Number', - type: 'number', - integral: false, - description: 'Your favorite number of all time', - warning: - 'Once you set this number, it can never be changed without severe consequences.', - nullable: true, - default: 7, - range: '(-100,100]', - units: 'BTC', - }, - 'unlucky-numbers': { - name: 'Unlucky Numbers', - type: 'list', - subtype: 'number', - description: 'Numbers that you like but are not your top favorite.', - spec: { - integral: false, - range: '[-100,200)', - }, - range: '[0,10]', - default: [2, 3], - }, - rpcsettings: { - name: 'RPC Settings', - type: 'object', - description: 'rpc username and password', - warning: 'Adding RPC users gives them special permissions on your node.', - spec: { - laws: { - name: 'Laws', - type: 'object', - description: 'the law of the realm', - spec: { - law1: { - name: 'First Law', - type: 'string', - description: 'the first law', - nullable: true, - masked: false, - copyable: true, - }, - law2: { - name: 'Second Law', - type: 'string', - description: 'the second law', - nullable: true, - masked: false, - copyable: true, - }, - }, - }, - rulemakers: { - name: 'Rule Makers', - type: 'list', - subtype: 'object', - description: 'the people who make the rules', - range: '[0,2]', + CB.Variants.of({ + internal: { name: 'Bitcoin Core', spec: CB.Config.of({}) }, + external: { + name: 'External Node', + spec: CB.Config.of({ + 'p2p-host': CB.Value.text({ + name: 'Public Address', + required: { + default: null, + }, + description: + 'The public address of your Bitcoin Core server', + }), + 'p2p-port': CB.Value.number({ + name: 'P2P Port', + description: + 'The port that your Bitcoin Core P2P server is bound to', + required: { + default: 8333, + }, + min: 0, + max: 65535, + integer: true, + }), + }), + }, + }), + ), + }), + ), + color: CB.Value.color({ + name: 'Color', + required: false, + }), + datetime: CB.Value.datetime({ + name: 'Datetime', + required: false, + }), + file: CB.Value.file({ + name: 'File', + required: false, + extensions: ['png', 'pdf'], + }), + users: CB.Value.multiselect({ + name: 'Users', default: [], - spec: { - 'unique-by': null, - spec: { - rulemakername: { - name: 'Rulemaker Name', - type: 'string', - description: 'the name of the rule maker', - nullable: false, - default: { - charset: 'a-g,2-9', - len: 12, - }, - masked: false, - copyable: false, + maxLength: 2, + disabled: ['matt'], + values: { + matt: 'Matt Hill', + alex: 'Alex Inkin', + blue: 'Blue J', + lucy: 'Lucy', + }, + }), + advanced: CB.Value.object( + { + name: 'Advanced', + description: 'Advanced settings', + }, + CB.Config.of({ + rpcsettings: CB.Value.object( + { + name: 'RPC Settings', + description: 'rpc username and password', + warning: + 'Adding RPC users gives them special permissions on your node.', }, - rulemakerip: { - name: 'Rulemaker IP', - type: 'string', - description: 'the ip of the rule maker', - nullable: false, - default: '192.168.1.0', - pattern: - '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', - 'pattern-description': 'may only contain numbers and periods', - masked: false, - copyable: true, - }, - }, - }, - }, - rpcuser: { - name: 'RPC Username', - type: 'string', - description: 'rpc username', - nullable: false, - default: 'defaultrpcusername', - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'must contain only letters.', - masked: false, - copyable: true, - }, - rpcpass: { - name: 'RPC User Password', - type: 'string', - description: 'rpc password', - nullable: false, - default: { - charset: 'a-z,A-Z,2-9', - len: 20, - }, - masked: true, - copyable: true, - }, - }, - }, - 'bitcoin-node': { - type: 'union', - default: 'internal', - tag: { - id: 'type', - 'variant-names': { - internal: 'Internal', - external: 'External', - }, - name: 'Bitcoin Node Settings', - description: 'Options
  • Item 1
  • Item 2
', - warning: 'Careful changing this', - }, - variants: { - internal: {}, - external: { - 'emergency-contact': { - name: 'Emergency Contact', - type: 'object', - description: 'The person to contact in case of emergency.', - spec: { - name: { - type: 'string', - name: 'Name', - nullable: false, - masked: false, - copyable: false, - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'Must contain only letters.', - }, - email: { - type: 'string', - name: 'Email', - nullable: false, - masked: false, - copyable: true, - }, - }, - }, - 'public-domain': { - name: 'Public Domain', - type: 'string', - description: 'the public address of the node', - nullable: false, - default: 'bitcoinnode.com', - pattern: '.*', - 'pattern-description': 'anything', - masked: false, - copyable: true, - }, - 'private-domain': { - name: 'Private Domain', - type: 'string', - description: 'the private address of the node', - nullable: false, - masked: true, - copyable: true, - }, - }, - }, - }, - port: { - name: 'Port', - type: 'number', - integral: true, - description: - 'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444', - nullable: false, - default: 8333, - range: '(0, 9998]', - }, - 'favorite-slogan': { - name: 'Favorite Slogan', - type: 'string', - description: - 'You most favorite slogan in the whole world, used for paying you.', - nullable: true, - masked: true, - copyable: true, - }, - rpcallowip: { - name: 'RPC Allowed IPs', - type: 'list', - subtype: 'string', - description: - 'external ip addresses that are authorized to access your Bitcoin node', - warning: - 'Any IP you allow here will have RPC access to your Bitcoin node.', - range: '[1,10]', - default: ['192.168.1.1'], - spec: { - masked: false, - copyable: false, - pattern: - '((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))', - 'pattern-description': 'must be a valid ipv4, ipv6, or domain name', - }, - }, - rpcauth: { - name: 'RPC Auth', - type: 'list', - subtype: 'string', - description: 'api keys that are authorized to access your Bitcoin node.', - range: '[0,*)', - default: [], - spec: { - masked: false, - copyable: false, - }, - }, - 'more-advanced': { - name: 'More Advanced', - type: 'object', - description: 'Advanced settings', - spec: { - notifications: { - name: 'Notification Preferences', - type: 'list', - subtype: 'enum', - description: 'how you want to be notified', - range: '[1,3]', - default: ['email'], - spec: { - 'value-names': { - email: 'EEEEmail', - text: 'Texxxt', - call: 'Ccccall', - push: 'PuuuusH', - webhook: 'WebHooookkeee', - }, - values: ['email', 'text', 'call', 'push', 'webhook'], - }, - }, - rpcsettings: { - name: 'RPC Settings', - type: 'object', - description: 'rpc username and password', - warning: - 'Adding RPC users gives them special permissions on your node.', - spec: { - laws: { - name: 'Laws', - type: 'object', - description: 'the law of the realm', - spec: { - law1: { - name: 'First Law', - type: 'string', - description: 'the first law', - nullable: true, - masked: false, - copyable: true, - }, - law2: { - name: 'Second Law', - type: 'string', - description: 'the second law', - nullable: true, - masked: false, - copyable: true, - }, - law4: { - name: 'Fourth Law', - type: 'string', - description: 'the fourth law', - nullable: true, - masked: false, - copyable: true, - }, - law3: { - name: 'Third Law', - type: 'list', - subtype: 'object', - description: 'the third law', - range: '[0,2]', - default: [], - spec: { - 'unique-by': null, - spec: { - lawname: { - name: 'Law Name', - type: 'string', - description: 'the name of the law maker', - nullable: false, - default: { - charset: 'a-g,2-9', - len: 12, - }, - masked: false, - copyable: false, - }, - lawagency: { - name: 'Law agency', - type: 'string', - description: 'the ip of the law maker', - nullable: false, - default: '192.168.1.0', - pattern: - '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', - 'pattern-description': - 'may only contain numbers and periods', - masked: false, - copyable: true, - }, + CB.Config.of({ + rpcuser2: CB.Value.text({ + name: 'RPC Username', + required: { + default: 'defaultrpcusername', + }, + description: 'rpc username', + patterns: [ + { + regex: '^[a-zA-Z]+$', + description: 'must contain only letters.', + }, + ], + }), + rpcuser: CB.Value.text({ + name: 'RPC Username', + required: { + default: 'defaultrpcusername', + }, + description: 'rpc username', + patterns: [ + { + regex: '^[a-zA-Z]+$', + description: 'must contain only letters.', + }, + ], + }), + rpcpass: CB.Value.text({ + name: 'RPC User Password', + required: { + default: { + charset: 'a-z,A-Z,2-9', + len: 20, }, }, - }, - law5: { - name: 'Fifth Law', - type: 'string', - description: 'the fifth law', - nullable: true, - masked: false, - copyable: true, - }, - }, + description: 'rpc password', + }), + rpcpass2: CB.Value.text({ + name: 'RPC User Password', + required: { + default: { + charset: 'a-z,A-Z,2-9', + len: 20, + }, + }, + description: 'rpc password', + }), + }), + ), + }), + ), + testnet: CB.Value.toggle({ + name: 'Testnet', + default: true, + description: + '
  • determines whether your node is running on testnet or mainnet
', + warning: 'Chain will have to resync!', + }), + 'object-list': CB.Value.list( + CB.List.obj( + { + name: 'Object List', + minLength: 0, + maxLength: 4, + default: [ + // { 'first-name': 'Admin', 'last-name': 'User', age: 40 }, + // { 'first-name': 'Admin2', 'last-name': 'User', age: 40 }, + ], + description: 'This is a list of objects, like users or something', }, - rulemakers: { - name: 'Rule Makers', - type: 'list', - subtype: 'object', - description: 'the people who make the rules', - range: '[0,2]', - default: [], - spec: { - 'unique-by': null, - spec: { - rulemakername: { - name: 'Rulemaker Name', - type: 'string', - description: 'the name of the rule maker', - nullable: false, + { + spec: CB.Config.of({ + 'first-name': CB.Value.text({ + name: 'First Name', + required: false, + description: 'User first name', + }), + 'last-name': CB.Value.text({ + name: 'Last Name', + required: { default: { charset: 'a-g,2-9', len: 12, }, - masked: false, - copyable: false, }, - rulemakerip: { - name: 'Rulemaker IP', - type: 'string', - description: 'the ip of the rule maker', - nullable: false, - default: '192.168.1.0', - pattern: - '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', - 'pattern-description': - 'may only contain numbers and periods', - masked: false, - copyable: true, + description: 'User first name', + patterns: [ + { + regex: '^[a-zA-Z]+$', + description: 'must contain only letters.', + }, + ], + }), + age: CB.Value.number({ + name: 'Age', + description: 'The age of the user', + warning: 'User must be at least 18.', + required: false, + min: 18, + integer: false, + }), + }), + displayAs: `I'm {{last-name}}, {{first-name}} {{last-name}}`, + uniqueBy: 'last-name', + }, + ), + ), + 'union-list': CB.Value.list( + CB.List.obj( + { + name: 'Union List', + minLength: 0, + maxLength: 2, + default: [], + description: 'This is a sample list of unions', + warning: 'If you change this, things may work.', + }, + { + spec: CB.Config.of({ + /* TODO: Convert range for this value ([0, 2])*/ + union: CB.Value.union( + { + name: 'Preference', + description: null, + warning: null, + required: { default: 'summer' }, }, + CB.Variants.of({ + summer: { + name: 'summer', + spec: CB.Config.of({ + 'favorite-tree': CB.Value.text({ + name: 'Favorite Tree', + required: { + default: 'Maple', + }, + description: 'What is your favorite tree?', + }), + 'favorite-flower': CB.Value.select({ + name: 'Favorite Flower', + description: 'Select your favorite flower', + required: { + default: 'none', + }, + values: { + none: 'none', + red: 'red', + blue: 'blue', + purple: 'purple', + }, + }), + }), + }, + winter: { + name: 'winter', + spec: CB.Config.of({ + 'like-snow': CB.Value.toggle({ + name: 'Like Snow?', + default: true, + description: 'Do you like snow or not?', + }), + }), + }, + }), + ), + }), + uniqueBy: 'preference', + }, + ), + ), + 'random-select': CB.Value.select({ + name: 'Random select', + description: 'This is not even real.', + warning: 'Be careful changing this!', + required: { + default: null, + }, + values: { + option1: 'option1', + option2: 'option2', + option3: 'option3', + }, + disabled: ['option2'], + }), + 'favorite-number': + /* TODO: Convert range for this value ((-100,100])*/ CB.Value.number({ + name: 'Favorite Number', + description: 'Your favorite number of all time', + warning: + 'Once you set this number, it can never be changed without severe consequences.', + required: { + default: 7, + }, + integer: false, + units: 'BTC', + }), + rpcsettings: CB.Value.object( + { + name: 'RPC Settings', + description: 'rpc username and password', + warning: + 'Adding RPC users gives them special permissions on your node.', + }, + CB.Config.of({ + laws: CB.Value.object( + { + name: 'Laws', + description: 'the law of the realm', + }, + CB.Config.of({ + law1: CB.Value.text({ + name: 'First Law', + required: false, + description: 'the first law', + }), + law2: CB.Value.text({ + name: 'Second Law', + required: false, + description: 'the second law', + }), + }), + ), + rulemakers: CB.Value.list( + CB.List.obj( + { + name: 'Rule Makers', + minLength: 0, + maxLength: 2, + description: 'the people who make the rules', + }, + { + spec: CB.Config.of({ + rulemakername: CB.Value.text({ + name: 'Rulemaker Name', + required: { + default: { + charset: 'a-g,2-9', + len: 12, + }, + }, + description: 'the name of the rule maker', + }), + rulemakerip: CB.Value.text({ + name: 'Rulemaker IP', + required: { + default: '192.168.1.0', + }, + description: 'the ip of the rule maker', + patterns: [ + { + regex: + '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', + description: 'may only contain numbers and periods', + }, + ], + }), + }), + }, + ), + ), + rpcuser: CB.Value.text({ + name: 'RPC Username', + required: { + default: 'defaultrpcusername', + }, + description: 'rpc username', + patterns: [ + { + regex: '^[a-zA-Z]+$', + description: 'must contain only letters.', + }, + ], + }), + rpcpass: CB.Value.text({ + name: 'RPC User Password', + required: { + default: { + charset: 'a-z,A-Z,2-9', + len: 20, }, }, - }, - rpcuser: { - name: 'RPC Username', - type: 'string', - description: 'rpc username', - nullable: false, - default: 'defaultrpcusername', - pattern: '^[a-zA-Z]+$', - 'pattern-description': 'must contain only letters.', - masked: false, - copyable: true, - }, - rpcpass: { - name: 'RPC User Password', - type: 'string', description: 'rpc password', - nullable: false, - default: { - charset: 'a-z,A-Z,2-9', - len: 20, - }, masked: true, - copyable: true, - }, + }), + }), + ), + 'bitcoin-node': CB.Value.union( + { + name: 'Bitcoin Node', + description: 'Options
  • Item 1
  • Item 2
', + warning: 'Careful changing this', + required: { default: 'internal' }, + disabled: ['fake'], }, - }, - }, - }, - } + CB.Variants.of({ + fake: { + name: 'Fake', + spec: CB.Config.of({}), + }, + internal: { + name: 'Internal', + spec: CB.Config.of({}), + }, + external: { + name: 'External', + spec: CB.Config.of({ + 'emergency-contact': CB.Value.object( + { + name: 'Emergency Contact', + description: 'The person to contact in case of emergency.', + }, + CB.Config.of({ + name: CB.Value.text({ + name: 'Name', + required: { + default: null, + }, + patterns: [ + { + regex: '^[a-zA-Z]+$', + description: 'Must contain only letters.', + }, + ], + }), + email: CB.Value.text({ + name: 'Email', + inputmode: 'email', + required: { + default: null, + }, + }), + }), + ), + 'public-domain': CB.Value.text({ + name: 'Public Domain', + required: { + default: 'bitcoinnode.com', + }, + description: 'the public address of the node', + patterns: [ + { + regex: '.*', + description: 'anything', + }, + ], + }), + 'private-domain': CB.Value.text({ + name: 'Private Domain', + required: { + default: null, + }, + description: 'the private address of the node', + masked: true, + inputmode: 'url', + }), + }), + }, + }), + ), + port: CB.Value.number({ + name: 'Port', + description: + 'the default port for your Bitcoin node. default: 8333, testnet: 18333, regtest: 18444', + required: { + default: 8333, + }, + min: 1, + max: 9998, + step: 1, + integer: true, + }), + 'favorite-slogan': CB.Value.text({ + name: 'Favorite Slogan', + generate: { + charset: 'a-z,A-Z,2-9', + len: 20, + }, + required: false, + description: + 'You most favorite slogan in the whole world, used for paying you.', + masked: true, + }), + rpcallowip: CB.Value.list( + CB.List.text( + { + name: 'RPC Allowed IPs', + minLength: 1, + maxLength: 10, + default: ['192.168.1.1'], + description: + 'external ip addresses that are authorized to access your Bitcoin node', + warning: + 'Any IP you allow here will have RPC access to your Bitcoin node.', + }, + { + patterns: [ + { + regex: + '((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|((^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$)|(^[a-z2-7]{16}\\.onion$)|(^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$))', + description: 'must be a valid ipv4, ipv6, or domain name', + }, + ], + }, + ), + ), + rpcauth: CB.Value.list( + CB.List.text( + { + name: 'RPC Auth', + description: + 'api keys that are authorized to access your Bitcoin node.', + }, + { + patterns: [], + }, + ), + ), + }), + ) export const MockConfig = { testnet: undefined, @@ -1381,8 +1243,7 @@ export module Mock { age: 60, }, ], - 'union-list': undefined, - 'random-enum': 'option2', + 'random-select': ['goodbye'], 'favorite-number': 0, rpcsettings: { laws: { @@ -1394,7 +1255,7 @@ export module Mock { rulemakers: [], }, 'bitcoin-node': { - type: 'internal', + selection: 'internal', }, port: 20, rpcallowip: undefined, @@ -1477,7 +1338,107 @@ export module Mock { }, }, currentDependencies: {}, - hosts: {}, + hosts: { + abcdefg: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 80: [ + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'ipv4', + value: '192.168.10.11', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.2', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'ipv6', + value: '[FE80:CD00:0000:0CDE:1257:0000:211E:729CD]', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'ipv6', + value: '[FE80:CD00:0000:0CDE:1257:0000:211E:1234]', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'onion', + hostname: { + value: 'bitcoin-p2p.onion', + port: 80, + sslPort: 443, + }, + }, + ], + }, + }, + bcdefgh: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 8332: [], + }, + }, + cdefghi: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 8333: [], + }, + }, + }, storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', diff --git a/web/projects/ui/src/app/services/api/api.types.ts b/web/projects/ui/src/app/services/api/api.types.ts index 96be4850b..10e8892fb 100644 --- a/web/projects/ui/src/app/services/api/api.types.ts +++ b/web/projects/ui/src/app/services/api/api.types.ts @@ -1,10 +1,9 @@ import { Dump } from 'patch-db-client' import { MarketplacePkg, StoreInfo } from '@start9labs/marketplace' import { PackagePropertiesVersioned } from 'src/app/util/properties.util' -import { ConfigSpec } from 'src/app/pkg-config/config-types' import { DataModel } from 'src/app/services/patch-db/data-model' import { StartOSDiskInfo, LogsRes, ServerLogsReq } from '@start9labs/shared' -import { T } from '@start9labs/start-sdk' +import { CT, T } from '@start9labs/start-sdk' import { WebSocketSubjectConfig } from 'rxjs/webSocket' export module RR { @@ -223,7 +222,7 @@ export module RR { export type InstallPackageRes = null export type GetPackageConfigReq = { id: string } // package.config.get - export type GetPackageConfigRes = { spec: ConfigSpec; config: object } + export type GetPackageConfigRes = { spec: CT.InputSpec; config: object } export type DrySetPackageConfigReq = { id: string; config: object } // package.config.set.dry export type DrySetPackageConfigRes = Breakages @@ -266,7 +265,7 @@ export module RR { export type DryConfigureDependencyRes = { oldConfig: object newConfig: object - spec: ConfigSpec + spec: CT.InputSpec } export type SideloadPackageReq = { diff --git a/web/projects/ui/src/app/services/api/embassy-api.service.ts b/web/projects/ui/src/app/services/api/embassy-api.service.ts index 735b8f1d1..a5bce8c62 100644 --- a/web/projects/ui/src/app/services/api/embassy-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-api.service.ts @@ -10,6 +10,8 @@ export abstract class ApiService { // for sideloading packages abstract uploadPackage(guid: string, body: Blob): Promise + abstract uploadFile(body: Blob): Promise + // websocket abstract openWebsocket$( diff --git a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts index 8cdd0d5e3..f25ee86ca 100644 --- a/web/projects/ui/src/app/services/api/embassy-live-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-live-api.service.ts @@ -29,7 +29,7 @@ export class LiveApiService extends ApiService { @Inject(PATCH_CACHE) private readonly cache$: Observable>, ) { super() - ;(window as any).rpcClient = this + ; (window as any).rpcClient = this } // for getting static files: ex icons, instructions, licenses @@ -53,6 +53,15 @@ export class LiveApiService extends ApiService { }) } + async uploadFile(body: Blob): Promise { + return this.httpRequest({ + method: Method.POST, + body, + url: `/rest/upload`, + responseType: 'text', + }) + } + // websocket openWebsocket$( diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 0a82ac850..284e3bab8 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -768,7 +768,7 @@ export class MockApiService extends ApiService { await pauseFor(2000) return { config: Mock.MockConfig, - spec: Mock.ConfigSpec, + spec: await Mock.getInputSpec(), } } @@ -1058,7 +1058,7 @@ export class MockApiService extends ApiService { return { oldConfig: Mock.MockConfig, newConfig: Mock.MockDependencyConfig, - spec: Mock.ConfigSpec, + spec: await Mock.getInputSpec(), } } @@ -1070,6 +1070,11 @@ export class MockApiService extends ApiService { } } + async uploadFile(body: Blob): Promise { + await pauseFor(2000) + return 'returnedhash' + } + private async initProgress(): Promise { const progress = JSON.parse(JSON.stringify(PROGRESS)) diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index 755799522..e24556771 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -185,7 +185,107 @@ export const mockPatchData: DataModel = { }, }, currentDependencies: {}, - hosts: {}, + hosts: { + abcdefg: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 80: [ + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'local', + value: 'adjective-noun.local', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.1', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'ipv4', + value: '10.0.0.2', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'eth0', + public: false, + hostname: { + kind: 'ipv6', + value: '[FE80:CD00:0000:0CDE:1257:0000:211E:729CD]', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'ip', + networkInterfaceId: 'wlan0', + public: false, + hostname: { + kind: 'ipv6', + value: '[FE80:CD00:0000:0CDE:1257:0000:211E:1234]', + port: null, + sslPort: 1234, + }, + }, + { + kind: 'onion', + hostname: { + value: 'bitcoin-p2p.onion', + port: 80, + sslPort: 443, + }, + }, + ], + }, + }, + bcdefgh: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 8332: [], + }, + }, + cdefghi: { + kind: 'multi', + bindings: [], + addresses: [], + hostnameInfo: { + 8333: [], + }, + }, + }, storeExposedDependents: [], registry: 'https://registry.start9.com/', developerKey: 'developer-key', diff --git a/web/projects/ui/src/app/services/form-dialog.service.ts b/web/projects/ui/src/app/services/form-dialog.service.ts new file mode 100644 index 000000000..69df946bb --- /dev/null +++ b/web/projects/ui/src/app/services/form-dialog.service.ts @@ -0,0 +1,41 @@ +import { inject, Injectable, Injector, Type } from '@angular/core' +import { TuiDialogOptions, TuiDialogService } from '@taiga-ui/core' +import { TuiDialogFormService, TuiPromptData } from '@taiga-ui/kit' +import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus' + +const PROMPT: Partial> = { + label: 'Unsaved Changes', + data: { + content: 'You have unsaved changes. Are you sure you want to leave?', + yes: 'Leave', + no: 'Cancel', + }, +} + +@Injectable({ providedIn: 'root' }) +export class FormDialogService { + private readonly dialogs = inject(TuiDialogService) + private readonly formService = new TuiDialogFormService(this.dialogs) + private readonly prompt = this.formService.withPrompt(PROMPT) + private readonly injector = Injector.create({ + parent: inject(Injector), + providers: [ + { + provide: TuiDialogFormService, + useValue: this.formService, + }, + ], + }) + + open(component: Type, options: Partial> = {}) { + this.dialogs + .open(new PolymorpheusComponent(component, this.injector), { + closeable: this.prompt, + dismissible: this.prompt, + ...options, + }) + .subscribe({ + complete: () => this.formService.markAsPristine(), + }) + } +} diff --git a/web/projects/ui/src/app/services/form.service.ts b/web/projects/ui/src/app/services/form.service.ts index ee8f0f136..3cd0ee591 100644 --- a/web/projects/ui/src/app/services/form.service.ts +++ b/web/projects/ui/src/app/services/form.service.ts @@ -7,24 +7,7 @@ import { ValidatorFn, Validators, } from '@angular/forms' -import { - ConfigSpec, - isValueSpecListOf, - ListValueSpecNumber, - ListValueSpecObject, - ListValueSpecOf, - ListValueSpecString, - ListValueSpecUnion, - UniqueBy, - ValueSpec, - ValueSpecEnum, - ValueSpecList, - ValueSpecNumber, - ValueSpecObject, - ValueSpecString, - ValueSpecUnion, -} from 'src/app/pkg-config/config-types' -import { getDefaultString, Range } from '../pkg-config/config-utilities' +import { CT, utils } from '@start9labs/start-sdk' const Mustache = require('mustache') @Injectable({ @@ -34,55 +17,54 @@ export class FormService { constructor(private readonly formBuilder: UntypedFormBuilder) {} createForm( - spec: ConfigSpec, - current: { [key: string]: any } = {}, + spec: CT.InputSpec, + current: Record = {}, ): UntypedFormGroup { return this.getFormGroup(spec, [], current) } - getUnionObject( - spec: ValueSpecUnion | ListValueSpecUnion, - selection: string, - current?: { [key: string]: any } | null, - ): UntypedFormGroup { - const { variants, tag } = spec - const { name, description, warning, 'variant-names': variantNames } = tag - - const enumSpec: ValueSpecEnum = { - type: 'enum', - name, - description, - warning, + getUnionSelectSpec( + spec: CT.ValueSpecUnion, + selection: string | null, + ): CT.ValueSpecSelect { + return { + ...spec, + type: 'select', default: selection, - values: Object.keys(variants), - 'value-names': variantNames, + values: Object.fromEntries( + Object.entries(spec.variants).map(([key, { name }]) => [key, name]), + ), } - return this.getFormGroup( - { [spec.tag.id]: enumSpec, ...spec.variants[selection] }, - [], - current, + } + + getUnionObject( + spec: CT.ValueSpecUnion, + selected: string | null, + ): UntypedFormGroup { + const group = this.getFormGroup({ + selection: this.getUnionSelectSpec(spec, selected), + }) + + group.setControl( + 'value', + this.getFormGroup(selected ? spec.variants[selected].spec : {}), ) + + return group } - getListItem(spec: ValueSpecList, entry: any) { - const listItemValidators = getListItemValidators(spec) - if (isValueSpecListOf(spec, 'string')) { - return this.formBuilder.control(entry, listItemValidators) - } else if (isValueSpecListOf(spec, 'number')) { - return this.formBuilder.control(entry, listItemValidators) - } else if (isValueSpecListOf(spec, 'enum')) { - return this.formBuilder.control(entry) - } else if (isValueSpecListOf(spec, 'object')) { - return this.getFormGroup(spec.spec.spec, listItemValidators, entry) - } else if (isValueSpecListOf(spec, 'union')) { - return this.getUnionObject(spec.spec, spec.spec.default, entry) + getListItem(spec: CT.ValueSpecList, entry?: any) { + if (CT.isValueSpecListOf(spec, 'text')) { + return this.formBuilder.control(entry, stringValidators(spec.spec)) + } else if (CT.isValueSpecListOf(spec, 'object')) { + return this.getFormGroup(spec.spec.spec, [], entry) } } - private getFormGroup( - config: ConfigSpec, + getFormGroup( + config: CT.InputSpec, validators: ValidatorFn[] = [], - current?: { [key: string]: any } | null, + current?: Record | null, ): UntypedFormGroup { let group: Record< string, @@ -95,150 +77,281 @@ export class FormService { } private getFormEntry( - spec: ValueSpec, + spec: CT.ValueSpec, currentValue?: any, ): UntypedFormGroup | UntypedFormArray | UntypedFormControl { - let validators: ValidatorFn[] let value: any switch (spec.type) { - case 'string': - validators = stringValidators(spec) + case 'text': if (currentValue !== undefined) { value = currentValue } else { - value = spec.default ? getDefaultString(spec.default) : null + value = spec.default ? utils.getDefaultString(spec.default) : null } - return this.formBuilder.control(value, validators) + return this.formBuilder.control(value, stringValidators(spec)) + case 'textarea': + value = currentValue || null + return this.formBuilder.control(value, textareaValidators(spec)) case 'number': - validators = numberValidators(spec) if (currentValue !== undefined) { value = currentValue } else { value = spec.default || null } - return this.formBuilder.control(value, validators) + return this.formBuilder.control(value, numberValidators(spec)) + case 'color': + if (currentValue !== undefined) { + value = currentValue + } else { + value = spec.default || null + } + return this.formBuilder.control(value, colorValidators(spec)) + case 'datetime': + if (currentValue !== undefined) { + value = currentValue + } else { + value = spec.default || null + } + return this.formBuilder.control(value, datetimeValidators(spec)) case 'object': return this.getFormGroup(spec.spec, [], currentValue) case 'list': - validators = listValidators(spec) const mapped = ( Array.isArray(currentValue) ? currentValue : (spec.default as any[]) ).map(entry => { return this.getListItem(spec, entry) }) - return this.formBuilder.array(mapped, validators) + return this.formBuilder.array(mapped, listValidators(spec)) + case 'file': + return this.formBuilder.control( + currentValue || null, + fileValidators(spec), + ) case 'union': - const currentSelection = currentValue?.[spec.tag.id] + const currentSelection = currentValue?.selection const isValid = !!spec.variants[currentSelection] return this.getUnionObject( spec, isValid ? currentSelection : spec.default, - isValid ? currentValue : undefined, ) - case 'boolean': - case 'enum': + case 'toggle': value = currentValue === undefined ? spec.default : currentValue return this.formBuilder.control(value) + case 'select': + value = currentValue === undefined ? spec.default : currentValue + return this.formBuilder.control(value, selectValidators(spec)) + case 'multiselect': + value = currentValue === undefined ? spec.default : currentValue + return this.formBuilder.control(value, multiselectValidators(spec)) default: return this.formBuilder.control(null) } } } -function getListItemValidators(spec: ValueSpecList) { - if (isValueSpecListOf(spec, 'string')) { - return stringValidators(spec.spec) - } else if (isValueSpecListOf(spec, 'number')) { - return numberValidators(spec.spec) - } -} +// function getListItemValidators(spec: CT.ValueSpecList) { +// if (CT.isValueSpecListOf(spec, 'text')) { +// return stringValidators(spec.spec) +// } +// } function stringValidators( - spec: ValueSpecString | ListValueSpecString, + spec: CT.ValueSpecText | CT.ListValueSpecText, ): ValidatorFn[] { const validators: ValidatorFn[] = [] - if (!(spec as ValueSpecString).nullable) { + if ((spec as CT.ValueSpecText).required) { validators.push(Validators.required) } - if (spec.pattern) { - validators.push(Validators.pattern(spec.pattern)) + validators.push(textLengthInRange(spec.minLength, spec.maxLength)) + + if (spec.patterns.length) { + spec.patterns.forEach(p => validators.push(Validators.pattern(p.regex))) } return validators } -function numberValidators( - spec: ValueSpecNumber | ListValueSpecNumber, -): ValidatorFn[] { +function textareaValidators(spec: CT.ValueSpecTextarea): ValidatorFn[] { + const validators: ValidatorFn[] = [] + + if (spec.required) { + validators.push(Validators.required) + } + + validators.push(textLengthInRange(spec.minLength, spec.maxLength)) + + return validators +} + +function colorValidators({ required }: CT.ValueSpecColor): ValidatorFn[] { + const validators: ValidatorFn[] = [Validators.pattern(/^#[0-9a-f]{6}$/i)] + + if (required) { + validators.push(Validators.required) + } + + return validators +} + +function datetimeValidators({ + required, + min, + max, +}: CT.ValueSpecDatetime): ValidatorFn[] { + const validators: ValidatorFn[] = [] + + if (required) { + validators.push(Validators.required) + } + + if (min) { + validators.push(datetimeMin(min)) + } + + if (max) { + validators.push(datetimeMax(max)) + } + + return validators +} + +function numberValidators(spec: CT.ValueSpecNumber): ValidatorFn[] { const validators: ValidatorFn[] = [] validators.push(isNumber()) - if (!(spec as ValueSpecNumber).nullable) { + if ((spec as CT.ValueSpecNumber).required) { validators.push(Validators.required) } - if (spec.integral) { + if (spec.integer) { validators.push(isInteger()) } - validators.push(numberInRange(spec.range)) + validators.push(numberInRange(spec.min, spec.max)) return validators } -function listValidators(spec: ValueSpecList): ValidatorFn[] { +function selectValidators(spec: CT.ValueSpecSelect): ValidatorFn[] { const validators: ValidatorFn[] = [] - validators.push(listInRange(spec.range)) - - validators.push(listItemIssue()) - - if (!isValueSpecListOf(spec, 'enum')) { - validators.push(listUnique(spec)) + if (spec.required) { + validators.push(Validators.required) } return validators } -export function numberInRange(stringRange: string): ValidatorFn { +function multiselectValidators(spec: CT.ValueSpecMultiselect): ValidatorFn[] { + const validators: ValidatorFn[] = [] + validators.push(listInRange(spec.minLength, spec.maxLength)) + return validators +} + +function listValidators(spec: CT.ValueSpecList): ValidatorFn[] { + const validators: ValidatorFn[] = [] + validators.push(listInRange(spec.minLength, spec.maxLength)) + validators.push(listItemIssue()) + return validators +} + +function fileValidators(spec: CT.ValueSpecFile): ValidatorFn[] { + const validators: ValidatorFn[] = [] + + if (spec.required) { + validators.push(Validators.required) + } + + return validators +} + +export function numberInRange( + min: number | null, + max: number | null, +): ValidatorFn { return control => { const value = control.value - if (!value) return null - try { - Range.from(stringRange).checkIncludes(value) - return null - } catch (e: any) { - return { numberNotInRange: { value: `Number must be ${e.message}` } } - } + if (typeof value !== 'number') return null + if (min && value < min) + return { + numberNotInRange: `Number must be greater than or equal to ${min}`, + } + if (max && value > max) + return { numberNotInRange: `Number must be less than or equal to ${max}` } + return null } } export function isNumber(): ValidatorFn { - return control => - !control.value || control.value == Number(control.value) - ? null - : { notNumber: { value: control.value } } + return ({ value }) => + !value || value == Number(value) ? null : { notNumber: 'Must be a number' } } export function isInteger(): ValidatorFn { - return control => - !control.value || control.value == Math.trunc(control.value) + return ({ value }) => + !value || value == Math.trunc(value) ? null - : { numberNotInteger: { value: control.value } } + : { numberNotInteger: 'Must be an integer' } } -export function listInRange(stringRange: string): ValidatorFn { +export function listInRange( + minLength: number | null, + maxLength: number | null, +): ValidatorFn { return control => { - try { - Range.from(stringRange).checkIncludes(control.value.length) - return null - } catch (e: any) { - return { listNotInRange: { value: `List must be ${e.message}` } } - } + const length = control.value.length + if (minLength && length < minLength) + return { + listNotInRange: `List must contain at least ${minLength} entries`, + } + if (maxLength && length > maxLength) + return { + listNotInRange: `List cannot contain more than ${maxLength} entries`, + } + return null + } +} + +export function datetimeMin(min: string): ValidatorFn { + return ({ value }) => { + if (!value) return null + + const date = new Date(value.length === 5 ? `2000-01-01T${value}` : value) + const minDate = new Date(min.length === 5 ? `2000-01-01T${min}` : min) + + return date < minDate ? { datetimeMin: `Minimum is ${min}` } : null + } +} + +export function datetimeMax(max: string): ValidatorFn { + return ({ value }) => { + if (!value) return null + + const date = new Date(value.length === 5 ? `2000-01-01T${value}` : value) + const maxDate = new Date(max.length === 5 ? `2000-01-01T${max}` : max) + + return date > maxDate ? { datetimeMin: `Maximum is ${max}` } : null + } +} + +export function textLengthInRange( + minLength: number | null, + maxLength: number | null, +): ValidatorFn { + return control => { + const value = control.value + if (value === null || value === undefined) return null + + const length = value.length + if (minLength && length < minLength) + return { listNotInRange: `Must be at least ${minLength} characters` } + if (maxLength && length > maxLength) + return { listNotInRange: `Cannot be great than ${maxLength} characters` } + return null } } @@ -247,36 +360,33 @@ export function listItemIssue(): ValidatorFn { const { controls } = parentControl as UntypedFormArray const problemChild = controls.find(c => c.invalid) if (problemChild) { - return { listItemIssue: { value: 'Invalid entries' } } + return { listItemIssue: 'Invalid entries' } } else { return null } } } -export function listUnique(spec: ValueSpecList): ValidatorFn { +export function listUnique(spec: CT.ValueSpecList): ValidatorFn { return control => { const list = control.value for (let idx = 0; idx < list.length; idx++) { for (let idx2 = idx + 1; idx2 < list.length; idx2++) { if (listItemEquals(spec, list[idx], list[idx2])) { + const objSpec = spec.spec let display1: string let display2: string - let uniqueMessage = isObjectOrUnion(spec.spec) - ? uniqueByMessageWrapper( - spec.spec['unique-by'], - spec.spec, - list[idx], - ) + let uniqueMessage = isObject(objSpec) + ? uniqueByMessageWrapper(objSpec.uniqueBy, objSpec) : '' - if (isObjectOrUnion(spec.spec) && spec.spec['display-as']) { + if (isObject(objSpec) && objSpec.displayAs) { display1 = `"${(Mustache as any).render( - spec.spec['display-as'], + objSpec.displayAs, list[idx], )}"` display2 = `"${(Mustache as any).render( - spec.spec['display-as'], + objSpec.displayAs, list[idx2], )}"` } else { @@ -285,9 +395,7 @@ export function listUnique(spec: ValueSpecList): ValidatorFn { } return { - listNotUnique: { - value: `${display1} and ${display2} are not unique.${uniqueMessage}`, - }, + listNotUnique: `${display1} and ${display2} are not unique.${uniqueMessage}`, } } } @@ -296,46 +404,40 @@ export function listUnique(spec: ValueSpecList): ValidatorFn { } } -function listItemEquals(spec: ValueSpecList, val1: any, val2: any): boolean { +function listItemEquals(spec: CT.ValueSpecList, val1: any, val2: any): boolean { // TODO: fix types - switch (spec.subtype) { - case 'string': - case 'number': - case 'enum': + switch (spec.spec.type) { + case 'text': return val1 == val2 case 'object': - const obj: ListValueSpecObject = spec.spec as any - - return listObjEquals(obj['unique-by'], obj, val1, val2) - case 'union': - const union: ListValueSpecUnion = spec.spec as any - - return unionEquals(union['unique-by'], union, val1, val2) + const obj = spec.spec + return listObjEquals(obj.uniqueBy, obj, val1, val2) default: return false } } -function itemEquals(spec: ValueSpec, val1: any, val2: any): boolean { +function itemEquals(spec: CT.ValueSpec, val1: any, val2: any): boolean { switch (spec.type) { - case 'string': + case 'text': + case 'textarea': case 'number': - case 'boolean': - case 'enum': + case 'toggle': + case 'select': return val1 == val2 case 'object': // TODO: 'unique-by' does not exist on ValueSpecObject, fix types return objEquals( (spec as any)['unique-by'], - spec as ValueSpecObject, + spec as CT.ValueSpecObject, val1, val2, ) case 'union': - // TODO: 'unique-by' does not exist on ValueSpecUnion, fix types + // TODO: 'unique-by' does not exist on CT.ValueSpecUnion, fix types return unionEquals( (spec as any)['unique-by'], - spec as ValueSpecUnion, + spec as CT.ValueSpecUnion, val1, val2, ) @@ -355,12 +457,12 @@ function itemEquals(spec: ValueSpec, val1: any, val2: any): boolean { } function listObjEquals( - uniqueBy: UniqueBy, - spec: ListValueSpecObject, + uniqueBy: CT.UniqueBy, + spec: CT.ListValueSpecObject, val1: any, val2: any, ): boolean { - if (uniqueBy === null) { + if (!uniqueBy) { return false } else if (typeof uniqueBy === 'string') { return itemEquals(spec.spec[uniqueBy], val1[uniqueBy], val2[uniqueBy]) @@ -383,12 +485,12 @@ function listObjEquals( } function objEquals( - uniqueBy: UniqueBy, - spec: ValueSpecObject, + uniqueBy: CT.UniqueBy, + spec: CT.ValueSpecObject, val1: any, val2: any, ): boolean { - if (uniqueBy === null) { + if (!uniqueBy) { return false } else if (typeof uniqueBy === 'string') { // TODO: fix types @@ -412,20 +514,19 @@ function objEquals( } function unionEquals( - uniqueBy: UniqueBy, - spec: ValueSpecUnion | ListValueSpecUnion, + uniqueBy: CT.UniqueBy, + spec: CT.ValueSpecUnion, val1: any, val2: any, ): boolean { - const tagId = spec.tag.id - const variant = spec.variants[val1[tagId]] - if (uniqueBy === null) { + const variantSpec = spec.variants[val1.selection].spec + if (!uniqueBy) { return false } else if (typeof uniqueBy === 'string') { - if (uniqueBy === tagId) { - return val1[tagId] === val2[tagId] + if (uniqueBy === 'selection') { + return val1.selection === val2.selection } else { - return itemEquals(variant[uniqueBy], val1[uniqueBy], val2[uniqueBy]) + return itemEquals(variantSpec[uniqueBy], val1[uniqueBy], val2[uniqueBy]) } } else if ('any' in uniqueBy) { for (let subSpec of uniqueBy.any) { @@ -446,20 +547,10 @@ function unionEquals( } function uniqueByMessageWrapper( - uniqueBy: UniqueBy, - spec: ListValueSpecObject | ListValueSpecUnion, - obj: Record, + uniqueBy: CT.UniqueBy, + spec: CT.ListValueSpecObject, ) { - let configSpec: ConfigSpec - if (isUnion(spec)) { - const tagId = spec.tag.id - configSpec = { - [tagId]: { name: spec.tag.name } as ValueSpec, - ...spec.variants[obj[tagId]], - } - } else { - configSpec = spec.spec - } + let configSpec = spec.spec const message = uniqueByMessage(uniqueBy, configSpec) if (message) { @@ -468,17 +559,17 @@ function uniqueByMessageWrapper( } function uniqueByMessage( - uniqueBy: UniqueBy, - configSpec: ConfigSpec, + uniqueBy: CT.UniqueBy, + configSpec: CT.InputSpec, outermost = true, ): string { let joinFunc const subSpecs: string[] = [] - if (uniqueBy === null) { + if (!uniqueBy) { return '' } else if (typeof uniqueBy === 'string') { return configSpec[uniqueBy] - ? (configSpec[uniqueBy] as ValueSpecObject).name + ? (configSpec[uniqueBy] as CT.ValueSpecObject).name : uniqueBy } else if ('any' in uniqueBy) { joinFunc = ' OR ' @@ -497,20 +588,15 @@ function uniqueByMessage( : '(' + ret + ')' } -function isObjectOrUnion( - spec: ListValueSpecOf, -): spec is ListValueSpecObject | ListValueSpecUnion { - // only lists of objects and unions have unique-by - return 'unique-by' in spec -} - -function isUnion(spec: any): spec is ListValueSpecUnion { - // only unions have tag - return !!spec.tag +function isObject( + spec: CT.ListValueSpecOf, +): spec is CT.ListValueSpecObject { + // only lists of objects have uniqueBy + return 'uniqueBy' in spec } export function convertValuesRecursive( - configSpec: ConfigSpec, + configSpec: CT.InputSpec, group: UntypedFormGroup, ) { Object.entries(configSpec).forEach(([key, valueSpec]) => { @@ -522,40 +608,27 @@ export function convertValuesRecursive( control.setValue( control.value || control.value === 0 ? Number(control.value) : null, ) - } else if (valueSpec.type === 'string') { + } else if (valueSpec.type === 'text' || valueSpec.type === 'textarea') { if (!control.value) control.setValue(null) } else if (valueSpec.type === 'object') { convertValuesRecursive(valueSpec.spec, group.get(key) as UntypedFormGroup) } else if (valueSpec.type === 'union') { const formGr = group.get(key) as UntypedFormGroup - const spec = valueSpec.variants[formGr.controls[valueSpec.tag.id].value] + const spec = valueSpec.variants[formGr.controls['selection'].value].spec convertValuesRecursive(spec, formGr) } else if (valueSpec.type === 'list') { const formArr = group.get(key) as UntypedFormArray const { controls } = formArr - if (valueSpec.subtype === 'number') { - controls.forEach(control => { - control.setValue(control.value ? Number(control.value) : null) - }) - } else if (valueSpec.subtype === 'string') { + if (valueSpec.spec.type === 'text') { controls.forEach(control => { if (!control.value) control.setValue(null) }) - } else if (valueSpec.subtype === 'object') { + } else if (valueSpec.spec.type === 'object') { controls.forEach(formGroup => { - const objectSpec = valueSpec.spec as ListValueSpecObject + const objectSpec = valueSpec.spec as CT.ListValueSpecObject convertValuesRecursive(objectSpec.spec, formGroup as UntypedFormGroup) }) - } else if (valueSpec.subtype === 'union') { - controls.forEach(formGroup => { - const unionSpec = valueSpec.spec as ListValueSpecUnion - const spec = - unionSpec.variants[ - (formGroup as UntypedFormGroup).controls[unionSpec.tag.id].value - ] - convertValuesRecursive(spec, formGroup as UntypedFormGroup) - }) } } }) diff --git a/web/projects/ui/src/app/services/modal.service.ts b/web/projects/ui/src/app/services/modal.service.ts deleted file mode 100644 index c34fce9a2..000000000 --- a/web/projects/ui/src/app/services/modal.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Injectable } from '@angular/core' -import { ModalController } from '@ionic/angular' -import { DependentInfo } from 'src/app/types/dependent-info' -import { AppConfigPage } from 'src/app/modals/app-config/app-config.page' - -@Injectable({ - providedIn: 'root', -}) -export class ModalService { - constructor(private readonly modalCtrl: ModalController) {} - - async presentModalConfig(componentProps: ComponentProps): Promise { - const modal = await this.modalCtrl.create({ - component: AppConfigPage, - componentProps, - }) - await modal.present() - } -} - -interface ComponentProps { - pkgId: string - dependentInfo?: DependentInfo -} diff --git a/web/projects/ui/src/app/util/configBuilderToSpec.ts b/web/projects/ui/src/app/util/configBuilderToSpec.ts new file mode 100644 index 000000000..1f75329c5 --- /dev/null +++ b/web/projects/ui/src/app/util/configBuilderToSpec.ts @@ -0,0 +1,9 @@ +import { CB } from '@start9labs/start-sdk' + +export async function configBuilderToSpec( + builder: + | CB.Config, unknown> + | CB.Config, never>, +) { + return builder.build({} as any) +} diff --git a/web/projects/ui/src/index.html b/web/projects/ui/src/index.html index 0248b7462..9c0cc2e89 100644 --- a/web/projects/ui/src/index.html +++ b/web/projects/ui/src/index.html @@ -21,10 +21,36 @@ /> + - + + Start OS +

Loading

+ +
diff --git a/web/projects/ui/src/styles.scss b/web/projects/ui/src/styles.scss index a00ca4ae2..fa4a6598e 100644 --- a/web/projects/ui/src/styles.scss +++ b/web/projects/ui/src/styles.scss @@ -347,4 +347,15 @@ p { svg:not(:root) { overflow: auto; -} \ No newline at end of file +} + +tui-dialog { + transform: translate3d(0, 0, 0); +} + +.g-buttons { + display: flex; + justify-content: flex-end; + gap: 16px; + margin-top: 24px; +}