diff --git a/CLEARNET.md b/CLEARNET.md deleted file mode 100644 index 457a2e4f7..000000000 --- a/CLEARNET.md +++ /dev/null @@ -1,40 +0,0 @@ -# Setting up clearnet for a service interface - -NOTE: this guide is for HTTPS only! Other configurations may require a more bespoke setup depending on the service. Please consult the service documentation or the Start9 Community for help with non-HTTPS applications - -## Initialize ACME certificate generation - -The following command will register your device with an ACME certificate provider, such as letsencrypt - -This only needs to be done once. - -``` -start-cli net acme init --provider=letsencrypt --contact="mailto:me@drbonez.dev" -``` - -- `provider` can be `letsencrypt`, `letsencrypt-staging` (useful if you're doing a lot of testing and want to avoid being rate limited), or the url of any provider that supports the [RFC8555](https://datatracker.ietf.org/doc/html/rfc8555) ACME api -- `contact` can be any valid contact url, typically `mailto:` urls. it can be specified multiple times to set multiple contacts - -## Whitelist a domain for ACME certificate acquisition - -The following command will tell the OS to use ACME certificates instead of system signed ones for the provided url. In this example, `testing.drbonez.dev` - -This must be done for every domain you wish to host on clearnet. - -``` -start-cli net acme domain add "testing.drbonez.dev" -``` - -## Forward clearnet port - -Go into your router settings, and map port 443 on your router to port 5443 on your start-os device. This one port should cover most use cases - -## Add domain to service host - -The following command will tell the OS to route https requests from the WAN to the provided hostname to the specified service. In this example, we are adding `testing.drbonez.dev` to the host `ui-multi` on the package `hello-world`. To see a list of available host IDs for a given package, run `start-cli package host list` - -This must be done for every domain you wish to host on clearnet. - -``` -start-cli package host hello-world address ui-multi add testing.drbonez.dev -``` diff --git a/Makefile b/Makefile index 3349bdc1d..882d22047 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ GZIP_BIN := $(shell which pigz || which gzip) TAR_BIN := $(shell which gtar || which tar) COMPILED_TARGETS := core/target/$(ARCH)-unknown-linux-musl/release/startbox core/target/$(ARCH)-unknown-linux-musl/release/containerbox system-images/compat/docker-images/$(ARCH).tar system-images/utils/docker-images/$(ARCH).tar system-images/binfmt/docker-images/$(ARCH).tar container-runtime/rootfs.$(ARCH).squashfs ALL_TARGETS := $(STARTD_SRC) $(ENVIRONMENT_FILE) $(GIT_HASH_FILE) $(VERSION_FILE) $(COMPILED_TARGETS) cargo-deps/$(ARCH)-unknown-linux-musl/release/startos-backup-fs $(shell if [ "$(PLATFORM)" = "raspberrypi" ]; then echo cargo-deps/aarch64-unknown-linux-musl/release/pi-beep; fi) $(shell /bin/bash -c 'if [[ "${ENVIRONMENT}" =~ (^|-)unstable($$|-) ]]; then echo cargo-deps/$(ARCH)-unknown-linux-musl/release/tokio-console; fi') $(PLATFORM_FILE) +REBUILD_TYPES = 1 ifeq ($(REMOTE),) mkdir = mkdir -p $1 @@ -80,6 +81,8 @@ clean: rm -rf container-runtime/dist rm -rf container-runtime/node_modules rm -f container-runtime/*.squashfs + if [ -d container-runtime/tmp/combined ] && mountpoint container-runtime/tmp/combined; then sudo umount container-runtime/tmp/combined; fi + if [ -d container-runtime/tmp/lower ] && mountpoint container-runtime/tmp/lower; then sudo umount container-runtime/tmp/lower; fi rm -rf container-runtime/tmp (cd sdk && make clean) rm -f ENVIRONMENT.txt @@ -226,7 +229,7 @@ container-runtime/node_modules/.package-lock.json: container-runtime/package.jso npm --prefix container-runtime ci touch container-runtime/node_modules/.package-lock.json -sdk/base/lib/osBindings/index.ts: core/startos/bindings/index.ts +sdk/base/lib/osBindings/index.ts: $(shell if [ "$(REBUILD_TYPES)" -ne 0 ]; then echo core/startos/bindings/index.ts; fi) mkdir -p sdk/base/lib/osBindings rsync -ac --delete core/startos/bindings/ sdk/base/lib/osBindings/ touch sdk/base/lib/osBindings/index.ts diff --git a/build/dpkg-deps/depends b/build/dpkg-deps/depends index f495df85d..4c2dbc557 100644 --- a/build/dpkg-deps/depends +++ b/build/dpkg-deps/depends @@ -11,6 +11,7 @@ cryptsetup curl dnsutils dmidecode +dnsutils dosfstools e2fsprogs ecryptfs-utils @@ -57,4 +58,5 @@ systemd-timesyncd tor util-linux vim +wireguard-tools wireless-tools diff --git a/build/lib/scripts/dhclient-exit-hook b/build/lib/scripts/dhclient-exit-hook deleted file mode 100755 index 8c4a97746..000000000 --- a/build/lib/scripts/dhclient-exit-hook +++ /dev/null @@ -1 +0,0 @@ -start-cli net dhcp update $interface \ No newline at end of file diff --git a/build/lib/scripts/enable-kiosk b/build/lib/scripts/enable-kiosk index 45bed5fe9..548542d23 100755 --- a/build/lib/scripts/enable-kiosk +++ b/build/lib/scripts/enable-kiosk @@ -4,7 +4,7 @@ set -e # install dependencies /usr/bin/apt update -/usr/bin/apt install --no-install-recommends -y xserver-xorg x11-xserver-utils xinit firefox-esr matchbox-window-manager libnss3-tools +/usr/bin/apt install --no-install-recommends -y xserver-xorg x11-xserver-utils xinit firefox-esr matchbox-window-manager libnss3-tools p11-kit-modules #Change a default preference set by stock debian firefox-esr sed -i 's|^pref("extensions.update.enabled", true);$|pref("extensions.update.enabled", false);|' /etc/firefox-esr/firefox-esr.js @@ -83,6 +83,8 @@ user_pref("toolkit.telemetry.updatePing.enabled", false); user_pref("toolkit.telemetry.cachedClientID", ""); EOF +ln -sf /usr/lib/$(uname -m)-linux-gnu/pkcs11/p11-kit-trust.so /usr/lib/firefox-esr/libnssckbi.so + # create kiosk script cat > /home/kiosk/kiosk.sh << 'EOF' #!/bin/sh @@ -99,7 +101,9 @@ done killall firefox-esr ) & matchbox-window-manager -use_titlebar no & -firefox-esr http://localhost --profile /home/kiosk/fx-profile +cp -r /home/kiosk/fx-profile /home/kiosk/fx-profile-tmp +firefox-esr http://localhost --profile /home/kiosk/fx-profile-tmp +rm -rf /home/kiosk/fx-profile-tmp EOF chmod +x /home/kiosk/kiosk.sh diff --git a/build/lib/scripts/gather_debug_info.sh b/build/lib/scripts/gather-debug-info similarity index 100% rename from build/lib/scripts/gather_debug_info.sh rename to build/lib/scripts/gather-debug-info diff --git a/build/lib/scripts/tor-check.sh b/build/lib/scripts/tor-check similarity index 100% rename from build/lib/scripts/tor-check.sh rename to build/lib/scripts/tor-check diff --git a/build/lib/scripts/wg-vps-setup b/build/lib/scripts/wg-vps-setup new file mode 100755 index 000000000..6c630bb46 --- /dev/null +++ b/build/lib/scripts/wg-vps-setup @@ -0,0 +1,367 @@ +#!/bin/bash + +# Colors for better output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[1;34m' +YELLOW='\033[1;33m' +NC='\033[0;37m' # No Color + +# --- Constants --- +readonly WIREGUARD_INSTALL_URL="https://raw.githubusercontent.com/start9labs/wg-vps-setup/master/wireguard-install.sh" +readonly SSH_KEY_DIR="/home/start9/.ssh" +readonly SSH_KEY_NAME="id_ed25519" +readonly SSH_PRIVATE_KEY="$SSH_KEY_DIR/$SSH_KEY_NAME" +readonly SSH_PUBLIC_KEY="$SSH_PRIVATE_KEY.pub" + +# Store original arguments +SCRIPT_ARGS=("$@") + +# --- Functions --- + +# Function to ensure script runs with root privileges by auto-elevating if needed +check_root() { + if [[ "$EUID" -ne 0 ]]; then + exec sudo "$0" "${SCRIPT_ARGS[@]}" + fi + sudo chown -R start9:startos "$SSH_KEY_DIR" +} + +# Function to print banner +print_banner() { + echo -e "${BLUE}" + echo "================================================" + echo -e " ${NC}StartOS WireGuard VPS Setup Tool${BLUE} " + echo "================================================" + echo -e "${NC}" +} + +# Function to print usage +print_usage() { + echo -e "Usage: $0 [-h] [-i IP] [-u USERNAME] [-p PORT] [-k SSH_KEY]" + echo "Options:" + echo " -h Show this help message" + echo " -i VPS IP address" + echo " -u SSH username (default: root)" + echo " -p SSH port (default: 22)" + echo " -k Path to the custom SSH private key (optional)" + echo " If no key is provided, the default key '$SSH_PRIVATE_KEY' will be used." +} + +# Function to display end message +display_end_message() { + echo -e "\n${BLUE}------------------------------------------------------------------${NC}" + echo -e "${NC}WireGuard server setup complete!" + echo -e "${BLUE}------------------------------------------------------------------${NC}" + echo -e "\n${YELLOW}To expose your services to the Clearnet, use the following commands on your StartOS system (replace placeholders):${NC}" + echo -e "\n ${YELLOW}1. Initialize ACME (This only needs to be done once):${NC}" + echo " start-cli net acme init --provider=letsencrypt --contact=mailto:your-email@example.com" + echo -e "\n ${YELLOW}2. Expose 'hello-world' on port 80 through VPS:${NC}" + echo " start-cli package host hello-world binding ui-multi set-public 80" + echo -e "\n ${YELLOW}3. Add a domain to your 'hello-world' service:${NC}" + echo " start-cli package host hello-world address ui-multi domain add your-domain.example.com --acme=letsencrypt" + echo -e "\n ${YELLOW}Replace '${NC}your-email@example.com${YELLOW}' with your actual email address, '${NC}your-domain.example.com${YELLOW}' with your actual domain and '${NC}hello-world${YELLOW}' with your actual service id.${NC}" + echo -e "${BLUE}------------------------------------------------------------------${NC}" +} + +# Function to validate IP address +validate_ip() { + local ip=$1 + if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + return 0 + else + return 1 + fi +} + +# Function for configuring SSH key authentication on remote server +configure_ssh_key_auth() { + echo -e "${BLUE}Configuring SSH key authentication on remote server...${NC}" + + ssh -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" ' + # Check if PubkeyAuthentication is commented out + if grep -q "^#PubkeyAuthentication" /etc/ssh/sshd_config; then + sed -i "s/^#PubkeyAuthentication.*/PubkeyAuthentication yes/" /etc/ssh/sshd_config + # Check if PubkeyAuthentication exists but is not enabled + elif grep -q "^PubkeyAuthentication" /etc/ssh/sshd_config; then + sed -i "s/^PubkeyAuthentication.*/PubkeyAuthentication yes/" /etc/ssh/sshd_config + # Add PubkeyAuthentication if it doesnt exist + else + echo "PubkeyAuthentication yes" >> /etc/ssh/sshd_config + fi + + # Configure AuthorizedKeysFile if needed + if grep -q "^#AuthorizedKeysFile" /etc/ssh/sshd_config; then + sed -i "s/^#AuthorizedKeysFile.*/AuthorizedKeysFile .ssh\/authorized_keys .ssh\/authorized_keys2/" /etc/ssh/sshd_config + elif ! grep -q "^AuthorizedKeysFile" /etc/ssh/sshd_config; then + echo "AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2" >> /etc/ssh/sshd_config + fi + + # Reload SSH service + systemctl reload sshd + ' +} + +# Function to handle StartOS connection (download only) +handle_startos_connection() { + echo -e "${BLUE}Fetching the WireGuard configuration file...${NC}" + + # Fetch the client configuration file + config_file=$(ssh -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" 'ls -t ~/*.conf 2>/dev/null | head -n 1') + if [ -z "$config_file" ]; then + echo -e "${RED}Error: No WireGuard configuration file found on the remote server.${NC}" + return 1 # Exit with error + fi + CONFIG_NAME=$(basename "$config_file") + + # Download the configuration file + if ! scp -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -P "$SSH_PORT" "$SSH_USER@$VPS_IP":~/"$CONFIG_NAME" ./; then + echo -e "${RED}Error: Failed to download the WireGuard configuration file.${NC}" + return 1 # Exit with error + fi + echo -e "${GREEN}WireGuard configuration file '$CONFIG_NAME' downloaded successfully.${NC}" + return 0 +} + +# Function to import WireGuard configuration +import_wireguard_config() { + local config_name="$1" + if [ -z "$config_name" ]; then + echo -e "${RED}Error: Configuration file name is missing.${NC}" + return 1 + fi + + local connection_name=$(basename "$config_name" .conf) #Extract base name without extension + + # Check if the connection with same name already exists + if nmcli connection show --active | grep -q "^${connection_name}\s"; then + read -r -p "A connection with the name '$connection_name' already exists. Do you want to override it? (y/N): " answer + if [[ "$answer" =~ ^[Yy]$ ]]; then + nmcli connection delete "$connection_name" + if [ $? -ne 0 ]; then + echo -e "${RED}Error: Failed to delete existing connection '$connection_name'.${NC}" + return 1 + fi + # Import if user chose to override or if connection did not exist + if ! nmcli connection import type wireguard file "$config_name"; then + echo -e "${RED}Error: Failed to import the WireGuard configuration using NetworkManager.${NC}" + rm -f "$config_name" + return 1 + fi + echo -e "${GREEN}WireGuard configuration '$config_name' has been imported to NetworkManager.${NC}" + rm -f "$config_name" + display_end_message + else + echo -e "${BLUE}Skipping import of the WireGuard configuration.${NC}" + rm -f "$config_name" + return 0 + fi + else + # Import if connection did not exist + if command -v nmcli &>/dev/null; then + if ! nmcli connection import type wireguard file "$config_name"; then + echo -e "${RED}Error: Failed to import the WireGuard configuration using NetworkManager.${NC}" + rm -f "$config_name" + return 1 + fi + echo -e "${GREEN}WireGuard configuration '$config_name' has been imported to NetworkManager.${NC}" + rm -f "$config_name" + display_end_message + else + echo -e "${YELLOW}Warning: NetworkManager 'nmcli' not found. Configuration file '$config_name' saved in current directory.${NC}" + echo -e "${YELLOW}Import the configuration to your StartOS manually by going to NetworkManager or using wg-quick up command${NC}" + fi + fi + return 0 +} + +# Function to download the install script +download_install_script() { + echo -e "${BLUE}Downloading latest WireGuard install script...${NC}" + # Download the script + if ! curl -sSf "$WIREGUARD_INSTALL_URL" -o wireguard-install.sh; then + echo -e "${RED}Failed to download WireGuard installation script.${NC}" + return 1 + fi + chmod +x wireguard-install.sh + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to chmod +x wireguard install script.${NC}" + return 1 + fi + echo -e "${GREEN}WireGuard install script downloaded successfully!${NC}" + return 0 +} + +# Function to install WireGuard +install_wireguard() { + echo -e "\n${BLUE}Installing WireGuard...${NC}" + + # Check if install script exist + if [ ! -f "wireguard-install.sh" ]; then + echo -e "${RED}WireGuard install script is missing. Did it failed to download?${NC}" + return 1 + fi + + # Run the remote install script and let it complete + if ! ssh -o ConnectTimeout=60 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" -t "$SSH_USER@$VPS_IP" "bash -c 'export TERM=xterm-256color; export STARTOS_HOSTNAME=$(hostname); bash ~/wireguard-install.sh'"; then + echo -e "${RED}WireGuard installation failed on remote server.${NC}" + return 1 + fi + + # Test if wireguard installed + if ! ssh -q -o BatchMode=yes -o ConnectTimeout=5 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" "test -f /etc/wireguard/wg0.conf"; then + echo -e "\n${RED}WireGuard installation failed because /etc/wireguard/wg0.conf is missing, which means the script removed it.${NC}" + return 1 + fi + + echo -e "\n${GREEN}WireGuard installation completed successfully!${NC}" + return 0 +} + +# --- Main Script --- +# Initialize variables +VPS_IP="" +SSH_USER="root" +SSH_PORT="22" +CUSTOM_SSH_KEY="" +CONFIG_NAME="" + +# Check if the script is run as root before anything else +check_root + +# Print banner +print_banner + +# Parse command line arguments +while getopts "hi:u:p:k:" opt; do + case $opt in + h) + print_usage + exit 0 + ;; + i) + VPS_IP=$OPTARG + ;; + u) + SSH_USER=$OPTARG + ;; + p) + SSH_PORT=$OPTARG + ;; + k) + CUSTOM_SSH_KEY=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + print_usage + exit 1 + ;; + esac +done + +# Check if custom SSH key is passed and update the private key variable +if [ -n "$CUSTOM_SSH_KEY" ]; then + if [ ! -f "$CUSTOM_SSH_KEY" ]; then + echo -e "${RED}Custom SSH key '$CUSTOM_SSH_KEY' not found.${NC}" + exit 1 + fi + SSH_PRIVATE_KEY="$CUSTOM_SSH_KEY" + SSH_PUBLIC_KEY="$CUSTOM_SSH_KEY.pub" +else + # Use default StartOS SSH key + if [ ! -f "$SSH_PRIVATE_KEY" ]; then + echo -e "${RED}No SSH key found at default location '$SSH_PRIVATE_KEY'. Please ensure StartOS SSH keys are properly configured.${NC}" + exit 1 + fi +fi + +if [ ! -f "$SSH_PUBLIC_KEY" ]; then + echo -e "${RED}Public key '$SSH_PUBLIC_KEY' not found. Please ensure both private and public keys exist.${NC}" + exit 1 +fi + +# If VPS_IP is not provided via command line, ask for it +if [ -z "$VPS_IP" ]; then + while true; do + echo -n "Please enter your VPS IP address: " + read VPS_IP + if validate_ip "$VPS_IP"; then + break + else + echo -e "${RED}Invalid IP address format. Please try again.${NC}" + fi + done +fi + +# Confirm SSH connection details +echo -e "\n${GREEN}Connection details:${NC}" +echo "VPS IP: $VPS_IP" +echo "SSH User: $SSH_USER" +echo "SSH Port: $SSH_PORT" + +echo -e "\n${GREEN}Proceeding with SSH key-based authentication...${NC}\n" + +# Copy SSH public key to the remote server +if ! ssh-copy-id -i "$SSH_PUBLIC_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP"; then + echo -e "${RED}Failed to copy SSH key to the remote server. Please ensure you have correct credentials.${NC}" + exit 1 +fi + +echo -e "${GREEN}SSH key-based authentication configured successfully!${NC}" + +# Test SSH connection using key-based authentication +echo -e "\nTesting SSH connection with key-based authentication..." +if ! ssh -q -o BatchMode=yes -o ConnectTimeout=5 -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -p "$SSH_PORT" "$SSH_USER@$VPS_IP" 'grep -q "^PubkeyAuthentication yes" /etc/ssh/sshd_config'; then + echo -e "\n${RED}SSH key-based authentication is not enabled on your VPS.${NC}" + echo -e "\n${YELLOW}Would you like this script to automatically enable SSH key authentication? (y/N):${NC} " + read -r answer + + if [[ "$answer" =~ ^[Yy]$ ]]; then + configure_ssh_key_auth + else + echo -e "\n${BLUE}------------------------------------------------------------------${NC}" + echo -e "${YELLOW}To manually enable SSH key authentication:${NC}" + echo -e "\n ${YELLOW}1. Connect to your VPS and edit sshd_config:${NC}" + echo " nano /etc/ssh/sshd_config" + echo -e "\n ${YELLOW}2. Find and uncomment or add the line:${NC}" + echo " PubkeyAuthentication yes" + echo -e "\n ${YELLOW}3. Restart the SSH service:${NC}" + echo " systemctl restart sshd" + echo -e "${BLUE}------------------------------------------------------------------${NC}" + echo -e "\n${YELLOW}Please enable SSH key authentication and run this script again.${NC}" + exit 1 + fi +fi +echo -e "${GREEN}SSH connection successful with key-based authentication!${NC}" + +# Download the WireGuard install script locally +if ! download_install_script; then + echo -e "${RED}Failed to download the latest install script. Exiting...${NC}" + exit 1 +fi + +# Upload the install script to the remote server +if ! scp -i "$SSH_PRIVATE_KEY" -o StrictHostKeyChecking=no -P "$SSH_PORT" wireguard-install.sh "$SSH_USER@$VPS_IP":~/; then + echo -e "${RED}Failed to upload WireGuard install script to the remote server.${NC}" + exit 1 +fi + +# Install WireGuard on remote server using the downloaded script +if ! install_wireguard; then + echo -e "${RED}WireGuard installation failed.${NC}" + exit 1 +fi + +# Remove the local install script +rm wireguard-install.sh >/dev/null 2>&1 + +# Handle the StartOS config (download) +if ! handle_startos_connection; then + echo -e "${RED}StartOS configuration download failed!${NC}" + exit 1 +fi + +# Import the configuration +if ! import_wireguard_config "$CONFIG_NAME"; then + echo -e "${RED}StartOS configuration import failed or skipped!${NC}" +fi diff --git a/container-runtime/package-lock.json b/container-runtime/package-lock.json index 696de8bf9..ccef1a4b4 100644 --- a/container-runtime/package-lock.json +++ b/container-runtime/package-lock.json @@ -37,7 +37,7 @@ }, "../sdk/dist": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha8", + "version": "0.3.6-beta.4", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -45,15 +45,13 @@ "@noble/hashes": "^1.4.0", "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", - "mime": "^4.0.3", - "ts-matches": "^5.5.1", + "mime-types": "^2.1.35", + "ts-matches": "^6.2.1", "yaml": "^2.2.2" }, "devDependencies": { - "@iarna/toml": "^2.2.5", "@types/jest": "^29.4.0", "@types/lodash.merge": "^4.6.2", - "copyfiles": "^2.4.1", "jest": "^29.4.3", "peggy": "^3.0.2", "prettier": "^3.2.5", @@ -61,12 +59,13 @@ "ts-node": "^10.9.1", "ts-pegjs": "^4.2.1", "tsx": "^4.7.1", - "typescript": "^5.0.4", - "yaml": "^2.2.2" + "typescript": "^5.0.4" } }, "node_modules/@ampproject/remapping": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -78,11 +77,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { @@ -90,7 +92,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -98,20 +102,23 @@ } }, "node_modules/@babel/core": { - "version": "7.25.2", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.8.tgz", + "integrity": "sha512-l+lkXCHS6tQEc5oUpK28xBOZ6+HwaH7YwoYQbLFiYb4nS2/l1tKnZEtEWkD0GuiYdvArf9qBS0XlQGXzPMsNqQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/traverse": "^7.26.8", + "@babel/types": "^7.26.8", + "@types/gensync": "^1.0.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -127,11 +134,13 @@ } }, "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.6", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -143,12 +152,16 @@ } }, "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -156,27 +169,32 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", + "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.2", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -186,6 +204,8 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -194,6 +214,8 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -202,30 +224,35 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.25.2", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7", - "@babel/traverse": "^7.25.2" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -235,27 +262,19 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -263,7 +282,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -271,7 +292,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -279,101 +302,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@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/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "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" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "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" - } - }, "node_modules/@babel/parser": { - "version": "7.25.3", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.26.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -384,6 +333,8 @@ }, "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": { @@ -395,6 +346,8 @@ }, "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "license": "MIT", "dependencies": { @@ -406,6 +359,8 @@ }, "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": { @@ -415,8 +370,42 @@ "@babel/core": "^7.0.0-0" } }, + "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" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { @@ -428,6 +417,8 @@ }, "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": { @@ -438,11 +429,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -453,6 +446,8 @@ }, "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": { @@ -464,6 +459,8 @@ }, "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": { @@ -475,6 +472,8 @@ }, "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": { @@ -486,6 +485,8 @@ }, "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": { @@ -497,6 +498,8 @@ }, "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": { @@ -508,6 +511,8 @@ }, "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": { @@ -517,8 +522,26 @@ "@babel/core": "^7.0.0-0" } }, + "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" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "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": { @@ -532,11 +555,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -546,28 +571,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", + "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.3", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", + "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.8", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/types": "^7.26.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -576,11 +605,13 @@ } }, "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.6", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -592,18 +623,21 @@ } }, "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/@babel/types": { - "version": "7.25.2", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -611,15 +645,21 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, "license": "MIT" }, "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==", "license": "ISC" }, "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": { @@ -635,6 +675,8 @@ }, "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": { @@ -643,6 +685,8 @@ }, "node_modules/@jest/console": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", "dependencies": { @@ -659,6 +703,8 @@ }, "node_modules/@jest/core": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "license": "MIT", "dependencies": { @@ -705,6 +751,8 @@ }, "node_modules/@jest/environment": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "license": "MIT", "dependencies": { @@ -719,6 +767,8 @@ }, "node_modules/@jest/expect": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "license": "MIT", "dependencies": { @@ -731,6 +781,8 @@ }, "node_modules/@jest/expect-utils": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "license": "MIT", "dependencies": { @@ -742,6 +794,8 @@ }, "node_modules/@jest/fake-timers": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -758,6 +812,8 @@ }, "node_modules/@jest/globals": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -772,6 +828,8 @@ }, "node_modules/@jest/reporters": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", "dependencies": { @@ -814,6 +872,8 @@ }, "node_modules/@jest/schemas": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", "dependencies": { @@ -825,6 +885,8 @@ }, "node_modules/@jest/source-map": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", "dependencies": { @@ -838,6 +900,8 @@ }, "node_modules/@jest/test-result": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", "dependencies": { @@ -852,6 +916,8 @@ }, "node_modules/@jest/test-sequencer": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", "dependencies": { @@ -866,6 +932,8 @@ }, "node_modules/@jest/transform": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -891,6 +959,8 @@ }, "node_modules/@jest/types": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { @@ -906,7 +976,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -920,6 +992,8 @@ }, "node_modules/@jridgewell/resolve-uri": { "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": { @@ -928,6 +1002,8 @@ }, "node_modules/@jridgewell/set-array": { "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": { @@ -936,11 +1012,15 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "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": { @@ -950,6 +1030,8 @@ }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", + "integrity": "sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==", "dev": true, "license": "MIT", "dependencies": { @@ -967,20 +1049,27 @@ } }, "node_modules/@noble/curves": { - "version": "1.4.0", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.4.0" + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.4.0", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", "license": "MIT", "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -988,6 +1077,8 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -1000,6 +1091,8 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -1008,6 +1101,8 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -1020,11 +1115,15 @@ }, "node_modules/@sinclair/typebox": { "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, "license": "MIT" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", "engines": { @@ -1036,6 +1135,8 @@ }, "node_modules/@sinonjs/commons": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1044,6 +1145,8 @@ }, "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1056,6 +1159,8 @@ }, "node_modules/@swc/cli": { "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@swc/cli/-/cli-0.1.65.tgz", + "integrity": "sha512-4NcgsvJVHhA7trDnMmkGLLvWMHu2kSy+qHx6QwRhhJhdiYdNUrhdp+ERxen73sYtaeEOYeLJcWrQ60nzKi6rpg==", "dev": true, "license": "MIT", "dependencies": { @@ -1086,13 +1191,15 @@ } }, "node_modules/@swc/core": { - "version": "1.5.28", + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.15.tgz", + "integrity": "sha512-/iFeQuNaGdK7mfJbQcObhAhsMqLT7qgMYl7jX2GEIO+VDTejESpzAyKwaMeYXExN8D6e5BRHBCe7M5YlsuzjDA==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.8" + "@swc/types": "^0.1.17" }, "engines": { "node": ">=10" @@ -1102,16 +1209,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.5.28", - "@swc/core-darwin-x64": "1.5.28", - "@swc/core-linux-arm-gnueabihf": "1.5.28", - "@swc/core-linux-arm64-gnu": "1.5.28", - "@swc/core-linux-arm64-musl": "1.5.28", - "@swc/core-linux-x64-gnu": "1.5.28", - "@swc/core-linux-x64-musl": "1.5.28", - "@swc/core-win32-arm64-msvc": "1.5.28", - "@swc/core-win32-ia32-msvc": "1.5.28", - "@swc/core-win32-x64-msvc": "1.5.28" + "@swc/core-darwin-arm64": "1.10.15", + "@swc/core-darwin-x64": "1.10.15", + "@swc/core-linux-arm-gnueabihf": "1.10.15", + "@swc/core-linux-arm64-gnu": "1.10.15", + "@swc/core-linux-arm64-musl": "1.10.15", + "@swc/core-linux-x64-gnu": "1.10.15", + "@swc/core-linux-x64-musl": "1.10.15", + "@swc/core-win32-arm64-msvc": "1.10.15", + "@swc/core-win32-ia32-msvc": "1.10.15", + "@swc/core-win32-x64-msvc": "1.10.15" }, "peerDependencies": { "@swc/helpers": "*" @@ -1122,13 +1229,187 @@ } } }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.15.tgz", + "integrity": "sha512-zFdZ6/yHqMCPk7OhLFqHy/MQ1EqJhcZMpNHd1gXYT7VRU3FaqvvKETrUlG3VYl65McPC7AhMRfXPyJ0JO/jARQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.15.tgz", + "integrity": "sha512-8g4yiQwbr8fxOOjKXdot0dEkE5zgE8uNZudLy/ZyAhiwiZ8pbJ8/wVrDOu6dqbX7FBXAoDnvZ7fwN1jk4C8jdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.15.tgz", + "integrity": "sha512-rl+eVOltl2+7WXOnvmWBpMgh6aO13G5x0U0g8hjwlmD6ku3Y9iRcThpOhm7IytMEarUp5pQxItNoPq+VUGjVHg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.15.tgz", + "integrity": "sha512-qxWEQeyAJMWJqjaN4hi58WMpPdt3Tn0biSK9CYRegQtvZWCbewr6v2agtSu5AZ2rudeH6OfCWAMDQQeSgn6PJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.15.tgz", + "integrity": "sha512-QcELd9/+HjZx0WCxRrKcyKGWTiQ0485kFb5w8waxcSNd0d9Lgk4EFfWWVyvIb5gIHpDQmhrgzI/yRaWQX4YSZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.15.tgz", + "integrity": "sha512-S1+ZEEn3+a/MiMeQqQypbwTGoBG8/sPoCvpNbk+uValyygT+jSn3U0xVr45FbukpmMB+NhBMqfedMLqKA0QnJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.15.tgz", + "integrity": "sha512-qW+H9g/2zTJ4jP7NDw4VAALY0ZlNEKzYsEoSj/HKi7k3tYEHjMzsxjfsY9I8WZCft23bBdV3RTCPoxCshaj1CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.15.tgz", + "integrity": "sha512-AhRB11aA6LxjIqut+mg7qsu/7soQDmbK6MKR9nP3hgBszpqtXbRba58lr24xIbBCMr+dpo6kgEapWt+t5Po6Zg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.15.tgz", + "integrity": "sha512-UGdh430TQwbDn6KjgvRTg1fO022sbQ4yCCHUev0+5B8uoBwi9a89qAz3emy2m56C8TXxUoihW9Y9OMfaRwPXUw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.10.15", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.15.tgz", + "integrity": "sha512-XJzBCqO1m929qbJsOG7FZXQWX26TnEoMctS3QjuCoyBmkHxxQmZsy78KjMes1aomTcKHCyFYgrRGWgVmk7tT4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/types": { - "version": "0.1.8", + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1137,6 +1418,8 @@ }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, "license": "MIT", "dependencies": { @@ -1148,11 +1431,15 @@ }, "node_modules/@tokenizer/token": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "dev": true, "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -1165,6 +1452,8 @@ }, "node_modules/@types/babel__generator": { "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1173,6 +1462,8 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -1182,6 +1473,8 @@ }, "node_modules/@types/babel__traverse": { "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "license": "MIT", "dependencies": { @@ -1190,6 +1483,8 @@ }, "node_modules/@types/cacheable-request": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, "license": "MIT", "dependencies": { @@ -1199,8 +1494,17 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/gensync": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.4.tgz", + "integrity": "sha512-C3YYeRQWp2fmq9OryX+FoDy8nXS6scQ7dPptD8LnFDAUNcKWJjXQKDNJD3HVm+kOUsXhTOkpi69vI4EuAr95bA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1209,16 +1513,22 @@ }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true, "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "license": "MIT", "dependencies": { @@ -1227,6 +1537,8 @@ }, "node_modules/@types/istanbul-reports": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1234,7 +1546,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1244,11 +1558,15 @@ }, "node_modules/@types/jsonpath": { "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/jsonpath/-/jsonpath-0.2.4.tgz", + "integrity": "sha512-K3hxB8Blw0qgW6ExKgMbXQv2UPZBoE2GqLpVY+yr7nMD2Pq86lsuIzyAaiQ7eMqFL5B6di6pxSkogLJEyEHoGA==", "dev": true, "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "license": "MIT", "dependencies": { @@ -1256,15 +1574,19 @@ } }, "node_modules/@types/node": { - "version": "20.14.2", + "version": "20.17.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.17.tgz", + "integrity": "sha512-/WndGO4kIfMicEQLTi/mDANUu/iVUhT7KboZPdEqqHQ4aTS+3qT3U5gIqWDFV+XouorjfgGqvKILJeHhuQgFYg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/responselike": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "license": "MIT", "dependencies": { @@ -1273,11 +1595,15 @@ }, "node_modules/@types/stack-utils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true, "license": "MIT" }, "node_modules/@types/yargs": { "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "license": "MIT", "dependencies": { @@ -1286,11 +1612,28 @@ }, "node_modules/@types/yargs-parser": { "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, "license": "MIT" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "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==", "license": "MIT", "dependencies": { "mime-types": "~2.1.34", @@ -1302,6 +1645,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -1316,6 +1661,8 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { @@ -1324,6 +1671,8 @@ }, "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": { @@ -1338,6 +1687,8 @@ }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -1350,6 +1701,8 @@ }, "node_modules/arch": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true, "funding": [ { @@ -1369,6 +1722,8 @@ }, "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": { @@ -1377,15 +1732,21 @@ }, "node_modules/array-flatten": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, "node_modules/async": { - "version": "3.2.5", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true, "license": "MIT" }, "node_modules/babel-jest": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", "dependencies": { @@ -1406,6 +1767,8 @@ }, "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": { @@ -1421,6 +1784,8 @@ }, "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { "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": { @@ -1436,6 +1801,8 @@ }, "node_modules/babel-plugin-istanbul/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, "license": "ISC", "bin": { @@ -1444,6 +1811,8 @@ }, "node_modules/babel-plugin-jest-hoist": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "license": "MIT", "dependencies": { @@ -1457,22 +1826,27 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -1480,6 +1854,8 @@ }, "node_modules/babel-preset-jest": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "license": "MIT", "dependencies": { @@ -1495,11 +1871,36 @@ }, "node_modules/balanced-match": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, + "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==", + "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/bin-check": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bin-check/-/bin-check-4.1.0.tgz", + "integrity": "sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==", "dev": true, "license": "MIT", "dependencies": { @@ -1512,6 +1913,8 @@ }, "node_modules/bin-version": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", + "integrity": "sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==", "dev": true, "license": "MIT", "dependencies": { @@ -1527,6 +1930,8 @@ }, "node_modules/bin-version-check": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-5.1.0.tgz", + "integrity": "sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==", "dev": true, "license": "MIT", "dependencies": { @@ -1542,7 +1947,9 @@ } }, "node_modules/bin-version/node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -1556,6 +1963,8 @@ }, "node_modules/bin-version/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": { @@ -1578,6 +1987,8 @@ }, "node_modules/bin-version/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": { @@ -1589,6 +2000,8 @@ }, "node_modules/bin-version/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": { @@ -1600,6 +2013,8 @@ }, "node_modules/bin-version/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": { @@ -1611,6 +2026,8 @@ }, "node_modules/bin-version/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": { @@ -1619,6 +2036,8 @@ }, "node_modules/bin-version/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": { @@ -1630,6 +2049,8 @@ }, "node_modules/bin-version/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": { @@ -1638,6 +2059,8 @@ }, "node_modules/bin-version/node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -1651,7 +2074,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.2", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -1662,7 +2087,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -1674,6 +2099,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -1682,6 +2109,8 @@ }, "node_modules/braces": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -1692,7 +2121,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -1710,10 +2141,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -1724,6 +2155,8 @@ }, "node_modules/bs-logger": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "license": "MIT", "dependencies": { @@ -1735,19 +2168,50 @@ }, "node_modules/bser": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -1755,6 +2219,8 @@ }, "node_modules/cacheable-lookup": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, "license": "MIT", "engines": { @@ -1763,6 +2229,8 @@ }, "node_modules/cacheable-request": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "license": "MIT", "dependencies": { @@ -1780,6 +2248,8 @@ }, "node_modules/cacheable-request/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": { @@ -1792,15 +2262,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/call-bind": { - "version": "1.0.7", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "license": "MIT", "dependencies": { - "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" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -1811,6 +2293,8 @@ }, "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": { @@ -1819,6 +2303,8 @@ }, "node_modules/camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "license": "MIT", "engines": { @@ -1826,7 +2312,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001650", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "dev": true, "funding": [ { @@ -1846,6 +2334,8 @@ }, "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": { @@ -1861,6 +2351,8 @@ }, "node_modules/char-regex": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", "engines": { @@ -1869,6 +2361,8 @@ }, "node_modules/ci-info": { "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -1882,12 +2376,16 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.3.1", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -1901,6 +2399,8 @@ }, "node_modules/clone-response": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "license": "MIT", "dependencies": { @@ -1912,6 +2412,8 @@ }, "node_modules/co": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", "engines": { @@ -1921,11 +2423,15 @@ }, "node_modules/collect-v8-coverage": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true, "license": "MIT" }, "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": { @@ -1937,11 +2443,15 @@ }, "node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "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": { @@ -1950,11 +2460,15 @@ }, "node_modules/concat-map": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "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==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -1965,6 +2479,8 @@ }, "node_modules/content-type": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1972,11 +2488,15 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { - "version": "0.6.0", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -1984,10 +2504,14 @@ }, "node_modules/cookie-signature": { "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, "node_modules/create-jest": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2008,6 +2532,8 @@ }, "node_modules/cross-spawn": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "license": "MIT", "dependencies": { @@ -2018,6 +2544,8 @@ }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", "engines": { "node": ">= 12" @@ -2025,6 +2553,8 @@ }, "node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -2032,6 +2562,8 @@ }, "node_modules/decompress-response": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2046,6 +2578,8 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, "license": "MIT", "engines": { @@ -2057,6 +2591,8 @@ }, "node_modules/dedent": { "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2070,10 +2606,14 @@ }, "node_modules/deep-is": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "license": "MIT" }, "node_modules/deepmerge": { "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": { @@ -2082,29 +2622,18 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, "license": "MIT", "engines": { "node": ">=10" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/depd": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2112,6 +2641,8 @@ }, "node_modules/destroy": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", "engines": { "node": ">= 0.8", @@ -2120,6 +2651,8 @@ }, "node_modules/detect-newline": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", "engines": { @@ -2128,18 +2661,38 @@ }, "node_modules/diff-sequences": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ee-first": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/ejs": { "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2153,12 +2706,16 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.5", + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", "dev": true, "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", "engines": { @@ -2170,11 +2727,15 @@ }, "node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/encodeurl": { - "version": "1.0.2", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2182,6 +2743,8 @@ }, "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": { @@ -2190,6 +2753,8 @@ }, "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": { @@ -2197,28 +2762,45 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "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==", "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild-plugin-resolve": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esbuild-plugin-resolve/-/esbuild-plugin-resolve-2.0.0.tgz", + "integrity": "sha512-eJy9B8yDW5X/J48eWtR1uVmv+DKfHvYYnrrcqQoe/nUkVHVOTZlJnSevkYyGOz6hI90t036Y5QIPDrGzmppxfg==", "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.2", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -2227,21 +2809,24 @@ }, "node_modules/escape-html": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/escape-string-regexp": { - "version": "5.0.0", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "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==", "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", @@ -2262,6 +2847,8 @@ }, "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==", "license": "BSD-3-Clause", "optional": true, "engines": { @@ -2270,6 +2857,8 @@ }, "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==", "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -2281,6 +2870,8 @@ }, "node_modules/estraverse": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -2288,6 +2879,8 @@ }, "node_modules/esutils": { "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -2295,13 +2888,37 @@ }, "node_modules/etag": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", "dev": true, "license": "MIT", "dependencies": { @@ -2319,6 +2936,8 @@ }, "node_modules/executable": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "license": "MIT", "dependencies": { @@ -2330,6 +2949,8 @@ }, "node_modules/exit": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" @@ -2337,6 +2958,8 @@ }, "node_modules/expect": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "license": "MIT", "dependencies": { @@ -2351,35 +2974,37 @@ } }, "node_modules/express": { - "version": "4.19.2", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -2388,10 +3013,16 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ext-list": { "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", + "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, "license": "MIT", "dependencies": { @@ -2403,6 +3034,8 @@ }, "node_modules/ext-name": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", + "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2414,7 +3047,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.2", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -2422,7 +3057,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -2430,15 +3065,21 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, "node_modules/fastq": { - "version": "1.17.1", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "dev": true, "license": "ISC", "dependencies": { @@ -2447,6 +3088,8 @@ }, "node_modules/fb-watchman": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2455,6 +3098,8 @@ }, "node_modules/fetch-blob": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", @@ -2476,6 +3121,8 @@ }, "node_modules/file-type": { "version": "17.1.6", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz", + "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==", "dev": true, "license": "MIT", "dependencies": { @@ -2492,6 +3139,8 @@ }, "node_modules/filebrowser": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/filebrowser/-/filebrowser-1.0.0.tgz", + "integrity": "sha512-RRONYpCDzbmWPhBX43T4dE+ptqLznJ7lKfbMaZLChB2i2ZIdFXoqT9qZTi70Dpq6fnJHuvcdeiRqMIPZKhVgTQ==", "license": "ISC", "dependencies": { "commander": "^2.9.0", @@ -2501,10 +3150,14 @@ }, "node_modules/filebrowser/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==", "license": "MIT" }, "node_modules/filelist": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2513,6 +3166,8 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -2524,6 +3179,8 @@ }, "node_modules/filename-reserved-regex": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", + "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "dev": true, "license": "MIT", "engines": { @@ -2535,6 +3192,8 @@ }, "node_modules/filenamify": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-5.1.1.tgz", + "integrity": "sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==", "dev": true, "license": "MIT", "dependencies": { @@ -2551,6 +3210,8 @@ }, "node_modules/fill-range": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -2561,11 +3222,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -2578,6 +3241,8 @@ }, "node_modules/find-up": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { @@ -2590,6 +3255,8 @@ }, "node_modules/find-versions": { "version": "5.1.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-5.1.0.tgz", + "integrity": "sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==", "dev": true, "license": "MIT", "dependencies": { @@ -2604,6 +3271,8 @@ }, "node_modules/formdata-polyfill": { "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -2614,6 +3283,8 @@ }, "node_modules/forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2621,6 +3292,8 @@ }, "node_modules/fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2628,12 +3301,17 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -2645,6 +3323,8 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2652,6 +3332,8 @@ }, "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": { @@ -2660,6 +3342,8 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -2667,14 +3351,21 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2685,14 +3376,31 @@ }, "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, "license": "MIT", "engines": { @@ -2701,6 +3409,9 @@ }, "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": { @@ -2720,6 +3431,8 @@ }, "node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -2731,6 +3444,8 @@ }, "node_modules/glob/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": { @@ -2740,6 +3455,8 @@ }, "node_modules/glob/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": { @@ -2751,6 +3468,8 @@ }, "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": { @@ -2758,10 +3477,12 @@ } }, "node_modules/gopd": { - "version": "1.0.1", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2769,6 +3490,8 @@ }, "node_modules/got": { "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2793,39 +3516,25 @@ }, "node_modules/graceful-fs": { "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==", "dev": true, "license": "ISC" }, "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/has-property-descriptors": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2836,6 +3545,8 @@ }, "node_modules/hasown": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -2846,16 +3557,22 @@ }, "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, "license": "MIT" }, "node_modules/http-cache-semantics": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true, "license": "BSD-2-Clause" }, "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==", "license": "MIT", "dependencies": { "depd": "2.0.0", @@ -2870,6 +3587,8 @@ }, "node_modules/http2-wrapper": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "license": "MIT", "dependencies": { @@ -2882,6 +3601,8 @@ }, "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": { @@ -2890,6 +3611,8 @@ }, "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==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -2900,6 +3623,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==", "dev": true, "funding": [ { @@ -2919,6 +3644,8 @@ }, "node_modules/import-local": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", "dependencies": { @@ -2937,6 +3664,8 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -2945,6 +3674,9 @@ }, "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.", "dev": true, "license": "ISC", "dependencies": { @@ -2954,10 +3686,14 @@ }, "node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "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==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -2965,11 +3701,15 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.15.0", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { @@ -2984,6 +3724,8 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -2992,6 +3734,8 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { @@ -3000,6 +3744,8 @@ }, "node_modules/is-generator-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", "engines": { @@ -3008,6 +3754,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==", "dev": true, "license": "MIT", "dependencies": { @@ -3019,6 +3767,8 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -3027,6 +3777,8 @@ }, "node_modules/is-plain-obj": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "license": "MIT", "engines": { @@ -3035,6 +3787,8 @@ }, "node_modules/is-stream": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { @@ -3043,11 +3797,15 @@ }, "node_modules/isexe": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/isomorphic-fetch": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", "license": "MIT", "dependencies": { "node-fetch": "^2.6.1", @@ -3056,6 +3814,8 @@ }, "node_modules/isomorphic-fetch/node_modules/node-fetch": { "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -3074,6 +3834,8 @@ }, "node_modules/istanbul-lib-coverage": { "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": { @@ -3082,6 +3844,8 @@ }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3097,6 +3861,8 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3110,6 +3876,8 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3122,11 +3890,13 @@ } }, "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.3.6", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3138,12 +3908,16 @@ } }, "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/istanbul-lib-source-maps/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": { @@ -3152,6 +3926,8 @@ }, "node_modules/istanbul-reports": { "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3164,6 +3940,8 @@ }, "node_modules/jake": { "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3181,6 +3959,8 @@ }, "node_modules/jake/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": { @@ -3190,6 +3970,8 @@ }, "node_modules/jake/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": { @@ -3201,6 +3983,8 @@ }, "node_modules/jest": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", "dependencies": { @@ -3226,6 +4010,8 @@ }, "node_modules/jest-changed-files": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "license": "MIT", "dependencies": { @@ -3238,7 +4024,9 @@ } }, "node_modules/jest-changed-files/node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -3252,6 +4040,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3274,6 +4064,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3285,6 +4077,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3296,6 +4090,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3307,6 +4103,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3315,6 +4113,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3326,6 +4126,8 @@ }, "node_modules/jest-changed-files/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": { @@ -3334,6 +4136,8 @@ }, "node_modules/jest-changed-files/node_modules/which": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { @@ -3348,6 +4152,8 @@ }, "node_modules/jest-circus": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", "dependencies": { @@ -3378,6 +4184,8 @@ }, "node_modules/jest-cli": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "license": "MIT", "dependencies": { @@ -3410,6 +4218,8 @@ }, "node_modules/jest-config": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3454,6 +4264,8 @@ }, "node_modules/jest-diff": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "license": "MIT", "dependencies": { @@ -3468,6 +4280,8 @@ }, "node_modules/jest-docblock": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", "dependencies": { @@ -3479,6 +4293,8 @@ }, "node_modules/jest-each": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3494,6 +4310,8 @@ }, "node_modules/jest-environment-node": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", "dependencies": { @@ -3510,6 +4328,8 @@ }, "node_modules/jest-get-type": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "license": "MIT", "engines": { @@ -3518,6 +4338,8 @@ }, "node_modules/jest-haste-map": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { @@ -3542,6 +4364,8 @@ }, "node_modules/jest-leak-detector": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", "dependencies": { @@ -3554,6 +4378,8 @@ }, "node_modules/jest-matcher-utils": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "license": "MIT", "dependencies": { @@ -3568,6 +4394,8 @@ }, "node_modules/jest-message-util": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { @@ -3587,6 +4415,8 @@ }, "node_modules/jest-mock": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "license": "MIT", "dependencies": { @@ -3600,6 +4430,8 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", "engines": { @@ -3616,6 +4448,8 @@ }, "node_modules/jest-regex-util": { "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", "engines": { @@ -3624,6 +4458,8 @@ }, "node_modules/jest-resolve": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", "dependencies": { @@ -3643,6 +4479,8 @@ }, "node_modules/jest-resolve-dependencies": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "license": "MIT", "dependencies": { @@ -3655,6 +4493,8 @@ }, "node_modules/jest-runner": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3686,6 +4526,8 @@ }, "node_modules/jest-runtime": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3718,6 +4560,8 @@ }, "node_modules/jest-snapshot": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", "dependencies": { @@ -3748,6 +4592,8 @@ }, "node_modules/jest-util": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { @@ -3764,6 +4610,8 @@ }, "node_modules/jest-validate": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", "dependencies": { @@ -3780,6 +4628,8 @@ }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", "engines": { @@ -3791,6 +4641,8 @@ }, "node_modules/jest-watcher": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "license": "MIT", "dependencies": { @@ -3809,6 +4661,8 @@ }, "node_modules/jest-worker": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { @@ -3823,6 +4677,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": { @@ -3837,11 +4693,15 @@ }, "node_modules/js-tokens": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "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": { @@ -3853,28 +4713,36 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "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==", "dev": true, "license": "MIT" }, "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": { @@ -3886,6 +4754,8 @@ }, "node_modules/jsonpath": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz", + "integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==", "license": "MIT", "dependencies": { "esprima": "1.2.2", @@ -3895,6 +4765,8 @@ }, "node_modules/jsonpath/node_modules/esprima": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz", + "integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3905,6 +4777,8 @@ }, "node_modules/keyv": { "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -3913,6 +4787,8 @@ }, "node_modules/kleur": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "license": "MIT", "engines": { @@ -3921,6 +4797,8 @@ }, "node_modules/leven": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", "engines": { @@ -3929,6 +4807,8 @@ }, "node_modules/levn": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2", @@ -3940,11 +4820,15 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, "node_modules/locate-path": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { @@ -3956,15 +4840,21 @@ }, "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, "license": "MIT" }, "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==", "license": "MIT" }, "node_modules/lowercase-keys": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "license": "MIT", "engines": { @@ -3973,6 +4863,8 @@ }, "node_modules/lru-cache": { "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "license": "ISC", "dependencies": { @@ -3982,6 +4874,8 @@ }, "node_modules/make-dir": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { @@ -3996,35 +4890,59 @@ }, "node_modules/make-error": { "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" }, "node_modules/makeerror": { "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "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==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "license": "MIT" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, "license": "MIT", "engines": { @@ -4033,13 +4951,17 @@ }, "node_modules/methods": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/micromatch": { - "version": "4.0.7", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { @@ -4052,6 +4974,8 @@ }, "node_modules/mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "bin": { "mime": "cli.js" @@ -4061,7 +4985,10 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -4069,6 +4996,8 @@ }, "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==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -4077,8 +5006,19 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", "engines": { @@ -4087,6 +5027,8 @@ }, "node_modules/mimic-response": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, "license": "MIT", "engines": { @@ -4094,7 +5036,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.4", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { @@ -4109,15 +5053,21 @@ }, "node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "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==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4125,6 +5075,8 @@ }, "node_modules/node-domexception": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "funding": [ { "type": "github", @@ -4142,6 +5094,8 @@ }, "node_modules/node-fetch": { "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -4158,16 +5112,22 @@ }, "node_modules/node-int64": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true, "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.18", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, "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==", "dev": true, "license": "MIT", "engines": { @@ -4176,6 +5136,8 @@ }, "node_modules/normalize-url": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, "license": "MIT", "engines": { @@ -4187,6 +5149,8 @@ }, "node_modules/npm-run-path": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", "dependencies": { @@ -4197,14 +5161,21 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -4215,6 +5186,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==", "dev": true, "license": "ISC", "dependencies": { @@ -4223,6 +5196,8 @@ }, "node_modules/onetime": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { @@ -4237,6 +5212,8 @@ }, "node_modules/optionator": { "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "license": "MIT", "dependencies": { "deep-is": "~0.1.3", @@ -4252,6 +5229,8 @@ }, "node_modules/os-filter-obj": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", + "integrity": "sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==", "dev": true, "license": "MIT", "dependencies": { @@ -4263,6 +5242,8 @@ }, "node_modules/p-cancelable": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, "license": "MIT", "engines": { @@ -4271,6 +5252,8 @@ }, "node_modules/p-finally": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { @@ -4279,6 +5262,8 @@ }, "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": { @@ -4293,6 +5278,8 @@ }, "node_modules/p-locate": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { @@ -4304,6 +5291,8 @@ }, "node_modules/p-locate/node_modules/p-limit": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "license": "MIT", "dependencies": { @@ -4318,6 +5307,8 @@ }, "node_modules/p-try": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", "engines": { @@ -4326,6 +5317,8 @@ }, "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": { @@ -4343,6 +5336,8 @@ }, "node_modules/parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4350,6 +5345,8 @@ }, "node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -4358,6 +5355,8 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { @@ -4366,6 +5365,8 @@ }, "node_modules/path-key": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -4374,15 +5375,21 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.7", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, "node_modules/peek-readable": { - "version": "5.0.0", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz", + "integrity": "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==", "dev": true, "license": "MIT", "engines": { @@ -4394,12 +5401,16 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -4411,6 +5422,8 @@ }, "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": { @@ -4419,6 +5432,8 @@ }, "node_modules/pirates": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "license": "MIT", "engines": { @@ -4427,6 +5442,8 @@ }, "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": { @@ -4438,12 +5455,16 @@ }, "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==", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { - "version": "3.3.2", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -4458,6 +5479,8 @@ }, "node_modules/pretty-format": { "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4471,6 +5494,8 @@ }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "engines": { @@ -4480,8 +5505,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prompts": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4494,6 +5531,8 @@ }, "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==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -4505,11 +5544,15 @@ }, "node_modules/pseudomap": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true, "license": "ISC" }, "node_modules/pump": { - "version": "3.0.0", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "license": "MIT", "dependencies": { @@ -4519,6 +5562,8 @@ }, "node_modules/pure-rand": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -4533,10 +5578,12 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.11.0", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -4547,6 +5594,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==", "dev": true, "funding": [ { @@ -4566,6 +5615,8 @@ }, "node_modules/quick-lru": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "license": "MIT", "engines": { @@ -4577,6 +5628,8 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4584,6 +5637,8 @@ }, "node_modules/raw-body": { "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==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -4597,28 +5652,37 @@ }, "node_modules/react-is": { "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, "license": "MIT" }, "node_modules/readable-stream": { - "version": "3.6.2", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "dev": true, "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": ">= 6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.3.tgz", + "integrity": "sha512-In3boYjBnbGVrLuuRu/Ath/H6h1jgk30nAsk/71tCare1dTVoe1oMBGRn5LGf0n3c1BcHwwAqpraxX4AUAP5KA==", "dev": true, "license": "MIT", "dependencies": { - "readable-stream": "^3.6.0" + "process": "^0.11.10", + "readable-stream": "^4.7.0" }, "engines": { "node": ">=8" @@ -4630,6 +5694,8 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -4637,28 +5703,37 @@ } }, "node_modules/resolve": { - "version": "1.22.8", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-alpn": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, "license": "MIT" }, "node_modules/resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", "dependencies": { @@ -4670,6 +5745,8 @@ }, "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": { @@ -4677,7 +5754,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.2", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", "engines": { @@ -4686,6 +5765,8 @@ }, "node_modules/responselike": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "license": "MIT", "dependencies": { @@ -4697,6 +5778,8 @@ }, "node_modules/reusify": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, "license": "MIT", "engines": { @@ -4706,6 +5789,8 @@ }, "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -4728,6 +5813,8 @@ }, "node_modules/safe-buffer": { "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", @@ -4746,10 +5833,14 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/semver": { - "version": "7.6.2", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -4761,6 +5852,8 @@ }, "node_modules/semver-regex": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", "dev": true, "license": "MIT", "engines": { @@ -4772,6 +5865,8 @@ }, "node_modules/semver-truncate": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-3.0.0.tgz", + "integrity": "sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -4785,7 +5880,9 @@ } }, "node_modules/send": { - "version": "0.18.0", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -4806,44 +5903,46 @@ "node": ">= 0.8.0" } }, + "node_modules/send/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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/serve-static": { - "version": "1.15.0", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "license": "MIT", - "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", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/shebang-command": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -4855,6 +5954,8 @@ }, "node_modules/shebang-regex": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { @@ -4862,13 +5963,69 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4879,16 +6036,22 @@ }, "node_modules/signal-exit": { "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "license": "ISC" }, "node_modules/sisteransi": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, "license": "MIT" }, "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": { @@ -4897,6 +6060,8 @@ }, "node_modules/sort-keys": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", "dev": true, "license": "MIT", "dependencies": { @@ -4908,6 +6073,8 @@ }, "node_modules/sort-keys-length": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", + "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", "dev": true, "license": "MIT", "dependencies": { @@ -4919,6 +6086,8 @@ }, "node_modules/source-map": { "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -4927,6 +6096,8 @@ }, "node_modules/source-map-support": { "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -4936,6 +6107,8 @@ }, "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": { @@ -4944,11 +6117,15 @@ }, "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, "license": "BSD-3-Clause" }, "node_modules/stack-utils": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4958,16 +6135,10 @@ "node": ">=10" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/static-eval": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz", + "integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==", "license": "MIT", "dependencies": { "escodegen": "^1.8.1" @@ -4975,6 +6146,8 @@ }, "node_modules/statuses": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4982,6 +6155,8 @@ }, "node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", "dependencies": { @@ -4990,6 +6165,8 @@ }, "node_modules/string-length": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5002,6 +6179,8 @@ }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -5015,6 +6194,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -5026,6 +6207,8 @@ }, "node_modules/strip-bom": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", "engines": { @@ -5034,6 +6217,8 @@ }, "node_modules/strip-eof": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "license": "MIT", "engines": { @@ -5042,6 +6227,8 @@ }, "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": { @@ -5050,6 +6237,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -5061,6 +6250,8 @@ }, "node_modules/strip-outer": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-2.0.0.tgz", + "integrity": "sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==", "dev": true, "license": "MIT", "engines": { @@ -5071,15 +6262,17 @@ } }, "node_modules/strtok3": { - "version": "7.0.0", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz", + "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==", "dev": true, "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0" + "peek-readable": "^5.1.3" }, "engines": { - "node": ">=14.16" + "node": ">=16" }, "funding": { "type": "github", @@ -5088,6 +6281,8 @@ }, "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": { @@ -5099,6 +6294,8 @@ }, "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==", "dev": true, "license": "MIT", "engines": { @@ -5110,6 +6307,8 @@ }, "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": { @@ -5123,6 +6322,8 @@ }, "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": { @@ -5132,6 +6333,8 @@ }, "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": { @@ -5143,19 +6346,15 @@ }, "node_modules/tmpl": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "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==", "dev": true, "license": "MIT", "dependencies": { @@ -5167,6 +6366,8 @@ }, "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==", "license": "MIT", "engines": { "node": ">=0.6" @@ -5174,6 +6375,8 @@ }, "node_modules/token-types": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -5190,10 +6393,14 @@ }, "node_modules/tr46": { "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/trim-repeated": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", + "integrity": "sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==", "dev": true, "license": "MIT", "dependencies": { @@ -5203,20 +6410,35 @@ "node": ">=12" } }, + "node_modules/trim-repeated/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ts-jest": { - "version": "29.2.4", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", + "bs-logger": "^0.2.6", "ejs": "^3.1.10", - "fast-json-stable-stringify": "2.x", + "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" @@ -5251,15 +6473,21 @@ } }, "node_modules/ts-matches": { - "version": "5.5.1", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-5.6.1.tgz", + "integrity": "sha512-1QXWQUa14MCgbz7vMg7i7eVPhMKB/5w8808nkN2sfnDkbG9nWYr9IwuTxX+h99yyawHYS53DewShA2RYCbSW4Q==", "license": "MIT" }, "node_modules/tslib": { - "version": "2.6.3", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "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==", "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2" @@ -5270,6 +6498,8 @@ }, "node_modules/type-detect": { "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "license": "MIT", "engines": { @@ -5278,6 +6508,8 @@ }, "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==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -5289,6 +6521,8 @@ }, "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==", "license": "MIT", "dependencies": { "media-typer": "0.3.0", @@ -5299,7 +6533,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5312,22 +6548,30 @@ }, "node_modules/underscore": { "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", "dev": true, "license": "MIT" }, "node_modules/unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -5345,8 +6589,8 @@ ], "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -5355,13 +6599,10 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "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==", "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -5369,6 +6610,8 @@ }, "node_modules/v8-to-istanbul": { "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", "dependencies": { @@ -5382,6 +6625,8 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5389,6 +6634,8 @@ }, "node_modules/walker": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5397,6 +6644,8 @@ }, "node_modules/web-streams-polyfill": { "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "license": "MIT", "engines": { "node": ">= 8" @@ -5404,14 +6653,20 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/whatwg-fetch": { "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "license": "MIT" }, "node_modules/whatwg-url": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -5420,6 +6675,8 @@ }, "node_modules/which": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5431,6 +6688,8 @@ }, "node_modules/word-wrap": { "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5438,6 +6697,8 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -5454,11 +6715,15 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "license": "ISC", "dependencies": { @@ -5471,6 +6736,8 @@ }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { @@ -5479,11 +6746,15 @@ }, "node_modules/yallist": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true, "license": "ISC" }, "node_modules/yaml": { - "version": "2.4.5", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -5494,6 +6765,8 @@ }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -5511,6 +6784,8 @@ }, "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==", "dev": true, "license": "ISC", "engines": { @@ -5519,6 +6794,8 @@ }, "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": { diff --git a/container-runtime/src/Adapters/EffectCreator.ts b/container-runtime/src/Adapters/EffectCreator.ts index 0123b0cbc..4bda0ed5d 100644 --- a/container-runtime/src/Adapters/EffectCreator.ts +++ b/container-runtime/src/Adapters/EffectCreator.ts @@ -216,12 +216,6 @@ export function makeEffects(context: EffectContext): Effects { }) as ReturnType }, - getPrimaryUrl(...[options]: Parameters) { - return rpcRound("get-primary-url", { - ...options, - callback: context.callbacks?.addCallback(options.callback) || null, - }) as ReturnType - }, getServicePortForward( ...[options]: Parameters ) { diff --git a/container-runtime/src/Adapters/RpcListener.ts b/container-runtime/src/Adapters/RpcListener.ts index 3e86e60d1..c2dc8bafe 100644 --- a/container-runtime/src/Adapters/RpcListener.ts +++ b/container-runtime/src/Adapters/RpcListener.ts @@ -212,16 +212,22 @@ export class RpcListener { s.on("data", (a) => Promise.resolve(a) .then((b) => b.toString()) - .then(logData("dataIn")) - .then(jsonParse) - .then(captureId) - .then((x) => this.dealWithInput(x)) - .catch(mapError) - .then(logData("response")) - .then(writeDataToSocket) - .catch((e) => { - console.error(`Major error in socket handling: ${e}`) - console.debug(`Data in: ${a.toString()}`) + .then((buf) => { + for (let s of buf.split("\n")) { + if (s) + Promise.resolve(s) + .then(logData("dataIn")) + .then(jsonParse) + .then(captureId) + .then((x) => this.dealWithInput(x)) + .catch(mapError) + .then(logData("response")) + .then(writeDataToSocket) + .catch((e) => { + console.error(`Major error in socket handling: ${e}`) + console.debug(`Data in: ${a.toString()}`) + }) + } }), ) }) @@ -390,7 +396,7 @@ export class RpcListener { .defaultToLazy(() => { console.warn( - `Coudln't parse the following input ${JSON.stringify(input)}`, + `Couldn't parse the following input ${JSON.stringify(input)}`, ) return { jsonrpc, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts index 26e4dd8bf..806216786 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/DockerProcedureContainer.ts @@ -43,7 +43,7 @@ export class DockerProcedureContainer { ) { const subcontainer = await SubContainer.of( effects, - { id: data.image }, + { imageId: data.image }, name, ) diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts index b6fe39854..fa76a1f84 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/MainLoop.ts @@ -113,7 +113,6 @@ export class MainLoop { })) .find((conf) => conf.internal == internalPort) await effects.bind({ - kind: "multi", id: interfaceId, internalPort, preferredExternalPort: torConf?.external || internalPort, diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts index 531b30cd2..2b32afd85 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/index.ts @@ -51,6 +51,7 @@ function todo(): never { const MANIFEST_LOCATION = "/usr/lib/startos/package/embassyManifest.json" export const EMBASSY_JS_LOCATION = "/usr/lib/startos/package/embassy.js" const EMBASSY_POINTER_PATH_PREFIX = "/embassyConfig" as utils.StorePath +const EMBASSY_DEPENDS_ON_PATH_PREFIX = "/embassyDependsOn" as utils.StorePath const matchResult = object({ result: any, @@ -314,7 +315,7 @@ export class SystemForEmbassy implements System { ) .catch(() => []), ) - await this.setDependencies(effects, oldDeps) + await this.setDependencies(effects, oldDeps, false) } async exit(): Promise { @@ -401,6 +402,7 @@ export class SystemForEmbassy implements System { return [ port, { + protocol: null, secure: null, preferredExternalPort: Number.parseInt( torPort || lanPort || String(port), @@ -425,7 +427,6 @@ export class SystemForEmbassy implements System { name: interfaceValue.name, id: `${id}-${internal}`, description: interfaceValue.description, - hasPrimary: false, type: interfaceValue.ui && (origin.scheme === "http" || origin.sslScheme === "https") @@ -664,7 +665,7 @@ export class SystemForEmbassy implements System { ), ) const dependsOn = answer["depends-on"] ?? answer.dependsOn ?? {} - await this.setDependencies(effects, dependsOn) + await this.setDependencies(effects, dependsOn, true) return } else if (setConfigValue.type === "script") { const moduleCode = await this.moduleCode @@ -687,48 +688,47 @@ export class SystemForEmbassy implements System { }), ) const dependsOn = answer["depends-on"] ?? answer.dependsOn ?? {} - await this.setDependencies(effects, dependsOn) + await this.setDependencies(effects, dependsOn, true) return } } private async setDependencies( effects: Effects, rawDepends: { [x: string]: readonly string[] }, + configuring: boolean, ) { - const dependsOn: Record = { + const storedDependsOn = (await effects.store.get({ + packageId: this.manifest.id, + path: EMBASSY_DEPENDS_ON_PATH_PREFIX, + })) as Record + + const requiredDeps = { ...Object.fromEntries( - Object.entries(this.manifest.dependencies || {})?.map((x) => [ - x[0], - null, - ]) || [], + Object.entries(this.manifest.dependencies || {}) + ?.filter((x) => x[1].requirement.type === "required") + .map((x) => [x[0], []]) || [], ), - ...rawDepends, } + + const dependsOn: Record = configuring + ? { + ...requiredDeps, + ...rawDepends, + } + : storedDependsOn + ? storedDependsOn + : requiredDeps + + await effects.store.set({ + path: EMBASSY_DEPENDS_ON_PATH_PREFIX, + value: dependsOn, + }) + await effects.setDependencies({ dependencies: Object.entries(dependsOn).flatMap( ([key, value]): T.Dependencies => { const dependency = this.manifest.dependencies?.[key] if (!dependency) return [] - if (value == null) { - const versionRange = dependency.version - if (dependency.requirement.type === "required") { - return [ - { - id: key, - versionRange, - kind: "running", - healthChecks: [], - }, - ] - } - return [ - { - kind: "exists", - id: key, - versionRange, - }, - ] - } const versionRange = dependency.version const kind = "running" return [ diff --git a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts index 7438070ea..5cbec945a 100644 --- a/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts +++ b/container-runtime/src/Adapters/Systems/SystemForEmbassy/polyfillEffects.ts @@ -109,7 +109,7 @@ export const polyfillEffects = ( return startSdk .runCommand( effects, - { id: manifest.main.image }, + { imageId: manifest.main.image }, commands, {}, commands.join(" "), @@ -165,7 +165,7 @@ export const polyfillEffects = ( await startSdk .runCommand( effects, - { id: manifest.main.image }, + { imageId: manifest.main.image }, commands, { mounts: [ @@ -207,7 +207,7 @@ export const polyfillEffects = ( await startSdk .runCommand( effects, - { id: manifest.main.image }, + { imageId: manifest.main.image }, commands, { mounts: [ diff --git a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts index 334764a87..1d38c83e6 100644 --- a/container-runtime/src/Adapters/Systems/SystemForStartOs.ts +++ b/container-runtime/src/Adapters/Systems/SystemForStartOs.ts @@ -74,8 +74,8 @@ export class SystemForStartOs implements System { async exit(): Promise {} async start(effects: Effects): Promise { + if (this.runningMain) return effects.constRetry = utils.once(() => effects.restart()) - if (this.runningMain) await this.stop() let mainOnTerm: () => Promise | undefined const started = async (onTerm: () => Promise) => { await effects.setMainStatus({ status: "running" }) @@ -98,8 +98,11 @@ export class SystemForStartOs implements System { async stop(): Promise { if (this.runningMain) { - await this.runningMain.stop() - this.runningMain = undefined + try { + await this.runningMain.stop() + } finally { + this.runningMain = undefined + } } } } diff --git a/container-runtime/src/index.ts b/container-runtime/src/index.ts index ec6a998f4..38c0aec1e 100644 --- a/container-runtime/src/index.ts +++ b/container-runtime/src/index.ts @@ -11,7 +11,7 @@ new RpcListener(getDependencies) /** -So, this is going to be sent into a running comtainer along with any of the other node modules that are going to be needed and used. +So, this is going to be sent into a running container along with any of the other node modules that are going to be needed and used. Once the container is started, we will go into a loading/ await state. This is the init system, and it will always be running, and it will be waiting for a command to be sent to it. @@ -38,5 +38,5 @@ There are /** TODO: -Should I seperate those adapter in/out? +Should I separate those adapter in/out? */ diff --git a/container-runtime/update-image.sh b/container-runtime/update-image.sh index 61429821c..a64b4371e 100755 --- a/container-runtime/update-image.sh +++ b/container-runtime/update-image.sh @@ -18,6 +18,13 @@ sudo mount -t overlay -olowerdir=tmp/lower,upperdir=tmp/upper,workdir=tmp/work o QEMU= if [ "$ARCH" != "$(uname -m)" ]; then QEMU=/usr/bin/qemu-${ARCH}-static + if ! which qemu-$ARCH-static > /dev/null; then + >&2 echo qemu-user-static is required for cross-platform builds + sudo umount tmp/combined + sudo umount tmp/lower + sudo rm -rf tmp + exit 1 + fi sudo cp $(which qemu-$ARCH-static) tmp/combined${QEMU} fi diff --git a/core/Cargo.lock b/core/Cargo.lock index cdafe81a1..a1e796a15 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -42,6 +42,17 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.7.8" @@ -92,9 +103,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -152,19 +163,29 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "arrayref" @@ -205,7 +226,7 @@ dependencies = [ "nom 7.1.3", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -217,7 +238,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -229,13 +250,13 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "async-acme" -version = "0.5.0" -source = "git+https://github.com/dr-bonez/async-acme.git#b9ff31ad900adc9086c0d1437ce51661d30856d2" +version = "0.6.0" +source = "git+https://github.com/dr-bonez/async-acme.git#0ddf25152237b5fc1726d977a7931e44513ce309" dependencies = [ "async-trait", "base64 0.22.1", @@ -244,16 +265,28 @@ dependencies = [ "log", "pem", "rcgen", - "ring", - "rustls 0.23.17", + "ring 0.17.8", + "rustls 0.23.21", "rustls-pemfile 2.2.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "x509-parser", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "1.9.0" @@ -261,15 +294,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue 2.5.0", - "event-listener", + "event-listener 2.5.3", "futures-core", ] [[package]] -name = "async-compression" -version = "0.4.17" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue 2.5.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli", "flate2", @@ -279,6 +324,108 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue 2.5.0", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue 2.5.0", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.0", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -298,18 +445,24 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] -name = "async-trait" -version = "0.1.83" +name = "async-task" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -335,28 +488,26 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7c2840b66236045acd2607d5866e274380afd87ef99d6226e961e2cb47df45" +checksum = "1ea835662a0af02443aa1396d39be523bbf8f11ee6fad20329607c480bea48c3" dependencies = [ "aws-lc-sys", - "mirai-annotations", "paste", "zeroize", ] [[package]] name = "aws-lc-sys" -version = "0.23.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3a619a9de81e1d7de1f1186dcba4506ed661a0e483d84410fdef0ee87b2f96" +checksum = "71b2ddd3ada61a305e1d8bb6c005d1eaa7d14d903681edfc400406d523a9b491" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", - "libc", "paste", ] @@ -373,7 +524,7 @@ dependencies = [ "futures-util", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "itoa", "matchit", "memchr", @@ -399,10 +550,10 @@ dependencies = [ "base64 0.22.1", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "itoa", "matchit", @@ -416,10 +567,10 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tokio", "tokio-tungstenite 0.24.0", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -451,37 +602,18 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "axum-server" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad46c3ec4e12f4a4b6835e173ba21c25e484c9d02b49770bf006ce5367c036" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.5.1", - "hyper-util", - "pin-project-lite", - "tokio", - "tower 0.4.13", - "tower-service", -] - [[package]] name = "backhand" version = "0.18.0" @@ -491,7 +623,7 @@ dependencies = [ "deku", "flate2", "rustc-hash", - "thiserror", + "thiserror 1.0.69", "tracing", "xz2", "zstd", @@ -520,8 +652,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be5951c75bdabb58753d140dd5802f12ff3a483cb2e16fb5276e111b94b19e87" dependencies = [ "concurrent-queue 1.2.4", - "event-listener", - "spin", + "event-listener 2.5.3", + "spin 0.9.8", ] [[package]] @@ -586,7 +718,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -599,7 +731,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.87", + "syn 2.0.96", "which", ] @@ -609,7 +741,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -618,6 +759,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -626,9 +773,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -676,9 +823,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" dependencies = [ "arrayref", "arrayvec 0.7.6", @@ -707,6 +854,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "brotli" version = "7.0.0" @@ -736,9 +896,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -754,9 +914,30 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] [[package]] name = "cache-padded" @@ -766,9 +947,9 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.2.1" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -798,9 +979,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -879,9 +1060,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -889,9 +1070,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -901,27 +1082,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cmake" -version = "0.1.51" +version = "0.1.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +checksum = "c682c223677e0e5b6b7f63a64b9351844c3f1b1678a68b7ee617e30fb082620e" dependencies = [ "cc", ] @@ -979,15 +1160,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ - "encode_unicode 0.3.6", - "lazy_static", + "encode_unicode", "libc", - "unicode-width 0.1.12", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -1036,18 +1217,18 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -1122,9 +1303,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1155,18 +1336,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1183,18 +1364,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -1202,7 +1383,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "futures-core", "mio", @@ -1317,7 +1498,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1341,7 +1522,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1352,14 +1533,20 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" [[package]] name = "deku" @@ -1383,7 +1570,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1420,7 +1607,7 @@ checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1433,6 +1620,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -1443,7 +1641,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1496,7 +1694,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -1655,12 +1853,6 @@ dependencies = [ "log", ] -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "encode_unicode" version = "1.0.0" @@ -1676,6 +1868,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -1685,7 +1883,28 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -1707,12 +1926,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1742,6 +1961,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue 2.5.0", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener 5.4.0", + "pin-project-lite", +] + [[package]] name = "exver" version = "0.2.0" @@ -1772,9 +2012,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock-rs" @@ -1826,7 +2066,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide 0.8.3", ] [[package]] @@ -1837,7 +2077,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -1956,6 +2196,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1964,7 +2217,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2027,7 +2280,7 @@ checksum = "75cec8bb4d3d32542cfcb9517f78366b52c17931e30d7ee1682c13686c19cee7" dependencies = [ "futures", "futures-rustls", - "hyper 1.5.1", + "hyper 1.5.2", "log", "serde", "serde_json", @@ -2035,7 +2288,17 @@ dependencies = [ "serde_urlencoded", "tokio", "tokio-rustls 0.25.0", - "webpki-roots 0.26.6", + "webpki-roots 0.26.7", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", ] [[package]] @@ -2068,9 +2331,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gpt" @@ -2078,7 +2341,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8283e7331b8c93b9756e0cfdbcfb90312852f953c6faf9bf741e684cc3b6ad69" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crc", "log", "uuid", @@ -2107,7 +2370,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -2125,8 +2388,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -2176,9 +2439,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hashlink" @@ -2252,6 +2515,57 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hickory-proto" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 1.0.3", + "ipnet", + "once_cell", + "rand 0.8.5", + "ring 0.16.20", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tokio-rustls 0.24.1", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand 0.8.5", + "resolv-conf", + "rustls 0.21.12", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tokio-rustls 0.24.1", + "tracing", +] + [[package]] name = "hifijson" version = "0.2.2" @@ -2278,11 +2592,22 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", ] [[package]] @@ -2298,9 +2623,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -2325,7 +2650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -2336,7 +2661,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -2361,9 +2686,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -2385,15 +2710,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -2406,18 +2731,18 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.5.1", + "http 1.2.0", + "hyper 1.5.2", "hyper-util", - "rustls 0.23.17", + "rustls 0.23.21", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", ] @@ -2427,7 +2752,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper 0.14.31", + "hyper 0.14.32", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2441,7 +2766,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-util", "native-tls", "tokio", @@ -2458,9 +2783,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.5.1", + "hyper 1.5.2", "pin-project-lite", "socket2", "tokio", @@ -2606,7 +2931,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -2682,17 +3007,29 @@ dependencies = [ [[package]] name = "imbl-sized-chunks" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63" +checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d" dependencies = [ "bitmaps", ] [[package]] name = "imbl-value" -version = "0.1.0" -source = "git+https://github.com/Start9Labs/imbl-value.git#3ce01b17ae5e756fc829ee5e3513a1b19b2a03fc" +version = "0.1.1" +source = "git+https://github.com/Start9Labs/imbl-value.git#1900943e17116def03bf00bff05cf12e54d810bc" +dependencies = [ + "imbl", + "serde", + "serde_json", + "yasi", +] + +[[package]] +name = "imbl-value" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3431be119ebf79f4bd0ce420dfa66aaeffbc4688f79c796237dc1f3b2d6d71" dependencies = [ "imbl", "serde", @@ -2739,12 +3076,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -2781,6 +3118,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + [[package]] name = "ipnet" version = "2.10.1" @@ -2824,7 +3173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ea1dc4bf0fb4904ba83ffdb98af3d9c325274e92e6e295e4151e86c96363e04" dependencies = [ "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2874,9 +3223,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jaq-core" @@ -2939,16 +3288,17 @@ dependencies = [ "regex", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "time", ] [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2956,7 +3306,7 @@ dependencies = [ name = "json-patch" version = "0.2.7-alpha.0" dependencies = [ - "imbl-value", + "imbl-value 0.1.2", "json-ptr", "serde", "treediff", @@ -2967,9 +3317,9 @@ name = "json-ptr" version = "0.1.0" dependencies = [ "imbl", - "imbl-value", + "imbl-value 0.1.2", "serde", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2977,7 +3327,7 @@ name = "jsonpath_lib" version = "0.3.0" source = "git+https://github.com/Start9Labs/jsonpath.git#1cacbd64afa2e1941a21fef06bad14317ba92f30" dependencies = [ - "imbl-value", + "imbl-value 0.1.1", "log", "serde", "serde_json", @@ -2999,7 +3349,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util", @@ -3029,7 +3379,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06cf485d4867e0714e35c1652e736bcf892d28fceecca01036764575db64ba84" dependencies = [ - "async-channel", + "async-channel 1.9.0", "futures", ] @@ -3045,7 +3395,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin", + "spin 0.9.8", ] [[package]] @@ -3069,15 +3419,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.164" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -3095,9 +3445,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", ] [[package]] @@ -3118,16 +3468,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e281a65eeba3d4503a2839252f86374528f9ceafe6fed97c1d3b52e1fb625c1" [[package]] -name = "linux-raw-sys" -version = "0.4.14" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litrs" @@ -3146,10 +3502,35 @@ dependencies = [ ] [[package]] -name = "log" -version = "0.4.22" +name = "lockfree-object-pool" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] [[package]] name = "lzma-sys" @@ -3162,6 +3543,70 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "mail-auth" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd9d657de66a3d5ac360c3eab8c9f5cac2565f2b97cc032d5de4c900ef470de" +dependencies = [ + "ahash 0.8.11", + "flate2", + "hickory-resolver", + "lru-cache", + "mail-builder", + "mail-parser", + "parking_lot", + "quick-xml", + "ring 0.17.8", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "zip", +] + +[[package]] +name = "mail-builder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f5871d5270ed80f2ee750b95600c8d69b05f8653ad3be913b2ad2e924fefcb" +dependencies = [ + "gethostname", +] + +[[package]] +name = "mail-parser" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c3b9e5d8b17faf573330bbc43b37d6e918c0a3bf8a88e7d0a220ebc84af9fc" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "mail-send" +version = "0.4.9" +source = "git+https://github.com/dr-bonez/mail-send.git?branch=main#57545dadab5808d59145d133de64f81b8ba01979" +dependencies = [ + "base64 0.22.1", + "gethostname", + "mail-auth", + "mail-builder", + "md5", + "rand 0.8.5", + "rustls 0.23.21", + "rustls-pki-types", + "smtp-proto", + "tokio", + "tokio-rustls 0.26.1", + "webpki-roots 0.26.7", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -3187,7 +3632,7 @@ dependencies = [ "bitvec 1.0.1", "serde", "serde-big-array", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3200,6 +3645,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + [[package]] name = "memchr" version = "2.7.4" @@ -3233,6 +3684,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -3256,32 +3716,25 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "models" version = "0.1.0" @@ -3301,16 +3754,18 @@ dependencies = [ "regex", "reqwest", "rpc-toolkit", + "rustls 0.23.21", "serde", "serde_json", "sqlx", "ssh-key", - "thiserror", + "thiserror 1.0.69", "tokio", "torut", "tracing", "ts-rs", "yasi", + "zbus", ] [[package]] @@ -3377,10 +3832,11 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "cfg_aliases", "libc", + "memoffset 0.9.1", ] [[package]] @@ -3550,7 +4006,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3599,7 +4055,7 @@ dependencies = [ "byteorder", "md-5", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -3608,7 +4064,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types", "libc", @@ -3625,7 +4081,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3656,6 +4112,16 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "overload" version = "0.1.1" @@ -3706,6 +4172,12 @@ dependencies = [ "sha2 0.10.8", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -3724,7 +4196,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets 0.52.6", ] @@ -3743,7 +4215,7 @@ dependencies = [ "fd-lock-rs", "futures", "imbl", - "imbl-value", + "imbl-value 0.1.2", "json-patch", "json-ptr", "lazy_static", @@ -3751,7 +4223,7 @@ dependencies = [ "patch-db-macro", "serde", "serde_cbor", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "tracing-error", @@ -3813,20 +4285,20 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" +checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror", + "thiserror 2.0.11", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd" +checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e" dependencies = [ "pest", "pest_generator", @@ -3834,22 +4306,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e" +checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "pest_meta" -version = "2.7.14" +version = "2.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d" +checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea" dependencies = [ "once_cell", "pest", @@ -3863,7 +4335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.0", ] [[package]] @@ -3883,29 +4355,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3913,6 +4385,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -3941,10 +4424,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] -name = "portable-atomic" -version = "1.9.0" +name = "polling" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue 2.5.0", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" [[package]] name = "powerfmt" @@ -3969,12 +4467,12 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -3984,11 +4482,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" dependencies = [ "csv", - "encode_unicode 1.0.0", + "encode_unicode", "is-terminal", "lazy_static", "term", - "unicode-width 0.1.12", + "unicode-width 0.1.14", ] [[package]] @@ -4011,9 +4509,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -4024,7 +4522,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "flate2", "hex", @@ -4039,20 +4537,20 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "hex", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.6.0", + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.8.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -4066,13 +4564,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" +checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4095,7 +4593,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4139,10 +4637,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] -name = "quote" -version = "1.0.37" +name = "quick-xml" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -4265,7 +4772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" dependencies = [ "pem", - "ring", + "ring 0.17.8", "time", "yasna", ] @@ -4287,11 +4794,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -4302,7 +4809,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4351,9 +4858,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -4363,10 +4870,10 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.5.1", + "hyper 1.5.2", "hyper-rustls", "hyper-tls", "hyper-util", @@ -4382,12 +4889,13 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper 1.0.2", "system-configuration", "tokio", "tokio-native-tls", "tokio-socks", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -4409,6 +4917,16 @@ dependencies = [ "url", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4419,6 +4937,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.8" @@ -4429,8 +4962,8 @@ dependencies = [ "cfg-if", "getrandom 0.2.15", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -4447,17 +4980,17 @@ dependencies = [ [[package]] name = "rpc-toolkit" -version = "0.2.3" -source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=refactor%2Fno-dyn-ctx#21e35d85fb8f5de0e046c7ab5266236c2b639a4b" +version = "0.3.0" +source = "git+https://github.com/Start9Labs/rpc-toolkit.git?branch=master#0747acc54b3dafa8689db1365fba5b28a03806b1" dependencies = [ "async-stream", "async-trait", "axum 0.7.9", "clap", "futures", - "http 1.1.0", + "http 1.2.0", "http-body-util", - "imbl-value", + "imbl-value 0.1.2", "itertools 0.12.1", "lazy_format", "lazy_static", @@ -4466,7 +4999,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "url", @@ -4475,9 +5008,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest 0.10.7", @@ -4547,15 +5080,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", - "errno 0.3.9", + "bitflags 2.8.0", + "errno 0.3.10", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4564,7 +5097,8 @@ version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ - "ring", + "log", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] @@ -4576,7 +5110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -4585,14 +5119,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.17" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "aws-lc-rs", "log", "once_cell", - "ring", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -4619,9 +5153,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" [[package]] name = "rustls-webpki" @@ -4629,8 +5163,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -4640,16 +5174,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "aws-lc-rs", - "ring", + "ring 0.17.8", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "rusty-fork" @@ -4665,18 +5199,17 @@ dependencies = [ [[package]] name = "rustyline-async" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9396d834c31f9fddd716e7c279e7cb70207092a1e59767918610f5c560c6eb" +checksum = "6fa3f78c2ea57b827be4c11adbfed26e5fe1b49fb6fb7826e2a9eebbc2e8db10" dependencies = [ "crossterm", - "futures-channel", "futures-util", "pin-project", "thingbuf", - "thiserror", + "thiserror 2.0.11", "unicode-segmentation", - "unicode-width 0.1.12", + "unicode-width 0.2.0", ] [[package]] @@ -4715,8 +5248,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -4739,7 +5272,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "core-foundation-sys", "libc", @@ -4748,9 +5281,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -4758,18 +5291,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -4793,22 +5326,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "itoa", "memchr", "ryu", @@ -4833,7 +5366,18 @@ checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c" dependencies = [ "percent-encoding", "serde", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -4859,15 +5403,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -4877,14 +5421,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -4893,7 +5437,7 @@ version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ce6afeda22f0b55dde2c34897bce76a629587348480384231205c14b59a01f" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "itoa", "libyml", "log", @@ -5016,6 +5560,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simple-logging" version = "2.0.2" @@ -5055,15 +5605,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] -name = "socket2" -version = "0.5.7" +name = "smtp-proto" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "51b8ad3dd187f0d4debab02ad65405a9919d6a4f7bce25bd64a258781063a53a" + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "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" @@ -5120,7 +5682,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener", + "event-listener 2.5.3", "futures-channel", "futures-core", "futures-intrusive", @@ -5128,7 +5690,7 @@ dependencies = [ "futures-util", "hashlink", "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "memchr", "once_cell", @@ -5141,7 +5703,7 @@ dependencies = [ "sha2 0.10.8", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tracing", @@ -5196,7 +5758,7 @@ checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "bytes", "chrono", @@ -5226,7 +5788,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "whoami", ] @@ -5239,7 +5801,7 @@ checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "chrono", "crc", @@ -5265,7 +5827,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.69", "tracing", "whoami", ] @@ -5296,9 +5858,9 @@ dependencies = [ [[package]] name = "sscanf" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a147d3cf7e723671ed11355b5b008c8019195f7fc902e213f5557d931e9f839d" +checksum = "c713ebd15ce561dd4a13ed62bc2a0368e16806fc30dcaf66ecf1256b2a3fdde6" dependencies = [ "const_format", "lazy_static", @@ -5308,17 +5870,17 @@ dependencies = [ [[package]] name = "sscanf_macro" -version = "0.4.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a37bdf8e90e77cc60f74473edf28d922ae2eacdd595e67724ccd2381774cc" +checksum = "84955aa74a157e5834d58a07be11af7f0ab923f0194a0bb2ea6b3db8b5d1611d" dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", "regex-syntax 0.6.29", "strsim 0.10.0", - "syn 2.0.87", - "unicode-width 0.1.12", + "syn 2.0.96", + "unicode-width 0.1.14", ] [[package]] @@ -5371,15 +5933,14 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "start-os" -version = "0.3.6-alpha.9" +version = "0.3.6-alpha.13" dependencies = [ - "aes", + "aes 0.7.5", "async-acme", "async-compression", "async-stream", "async-trait", "axum 0.7.9", - "axum-server", "backhand", "barrage", "base32 0.5.1", @@ -5394,6 +5955,7 @@ dependencies = [ "color-eyre", "console", "console-subscriber", + "const_format", "cookie", "cookie_store", "der", @@ -5410,14 +5972,15 @@ dependencies = [ "helpers", "hex", "hmac", - "http 1.1.0", + "http 1.2.0", "http-body-util", + "hyper 1.5.2", "hyper-util", "id-pool", "imbl", - "imbl-value", + "imbl-value 0.1.2", "include_dir", - "indexmap 2.6.0", + "indexmap 2.7.0", "indicatif", "integer-encoding", "ipnet", @@ -5433,7 +5996,9 @@ dependencies = [ "lazy_static", "libc", "log", + "mail-send", "mbrman", + "mio", "models", "new_mime_guess", "nix 0.29.0", @@ -5461,6 +6026,8 @@ dependencies = [ "rpassword", "rpc-toolkit", "rust-argon2", + "rustls 0.23.21", + "rustls-pki-types", "rustyline-async", "semver", "serde", @@ -5478,9 +6045,9 @@ dependencies = [ "ssh-key", "tar", "textwrap", - "thiserror", + "thiserror 1.0.69", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-socks", "tokio-stream", "tokio-tar", @@ -5502,6 +6069,7 @@ dependencies = [ "url", "urlencoding", "uuid", + "zbus", "zeroize", ] @@ -5566,9 +6134,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.87" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -5583,9 +6151,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] @@ -5598,7 +6166,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5607,7 +6175,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation", "system-configuration-sys", ] @@ -5636,17 +6204,18 @@ checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" dependencies = [ "filetime", "libc", - "xattr 1.3.1", + "xattr 1.4.0", ] [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom 0.2.15", "once_cell", "rustix", "windows-sys 0.59.0", @@ -5680,7 +6249,7 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "smawk", "unicode-linebreak", - "unicode-width 0.1.12", + "unicode-width 0.1.14", ] [[package]] @@ -5699,7 +6268,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -5710,7 +6288,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -5736,9 +6325,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -5757,9 +6346,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -5786,9 +6375,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -5801,9 +6390,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -5830,13 +6419,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -5849,6 +6438,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.25.0" @@ -5862,12 +6461,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.17", - "rustls-pki-types", + "rustls 0.23.21", "tokio", ] @@ -5879,15 +6477,15 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror", + "thiserror 1.0.69", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -5937,9 +6535,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -5987,7 +6585,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -6000,11 +6598,11 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.20", + "winnow 0.6.24", ] [[package]] @@ -6021,7 +6619,7 @@ dependencies = [ "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", - "hyper 0.14.31", + "hyper 0.14.32", "hyper-timeout", "percent-encoding", "pin-project", @@ -6075,14 +6673,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -6103,9 +6701,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -6115,20 +6713,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -6136,9 +6734,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -6156,9 +6754,9 @@ dependencies = [ [[package]] name = "tracing-journald" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" +checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" dependencies = [ "libc", "tracing-core", @@ -6178,9 +6776,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -6221,7 +6819,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "smallvec", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -6242,7 +6840,7 @@ dependencies = [ "futures-executor", "futures-util", "serde", - "thiserror", + "thiserror 1.0.69", "time", "tokio", "toml 0.7.8", @@ -6261,7 +6859,7 @@ name = "ts-rs" version = "8.1.0" source = "git+https://github.com/dr-bonez/ts-rs.git?branch=feature%2Ftop-level-as#7ae88ade90b5e724159048a663a0bdb04bed27f7" dependencies = [ - "thiserror", + "thiserror 1.0.69", "ts-rs-macros", ] @@ -6273,7 +6871,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "termcolor", ] @@ -6297,13 +6895,13 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "native-tls", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -6317,12 +6915,12 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "utf-8", ] @@ -6343,7 +6941,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -6358,6 +6956,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "winapi", +] + [[package]] name = "unarray" version = "0.1.4" @@ -6366,21 +6975,21 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-linebreak" @@ -6411,9 +7020,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" @@ -6443,6 +7052,12 @@ dependencies = [ "libc", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -6451,9 +7066,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.3" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna 1.0.3", @@ -6493,18 +7108,18 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" dependencies = [ "getrandom 0.2.15", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -6566,47 +7181,48 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6614,22 +7230,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -6646,9 +7265,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -6672,9 +7291,9 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -6697,10 +7316,16 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "wasite", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -6930,13 +7555,23 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.20" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -6977,7 +7612,7 @@ dependencies = [ "nom 7.1.3", "oid-registry", "rusticata-macros", - "thiserror", + "thiserror 1.0.69", "time", ] @@ -6992,15 +7627,25 @@ dependencies = [ [[package]] name = "xattr" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ "libc", "linux-raw-sys", "rustix", ] +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "xz2" version = "0.1.7" @@ -7019,7 +7664,7 @@ dependencies = [ "anyhow", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -7045,9 +7690,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", @@ -7057,16 +7702,79 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] +[[package]] +name = "zbus" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "192a0d989036cd60a1e91a54c9851fb9ad5bd96125d41803eed79d2e2ef74bd7" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener 5.4.0", + "futures-core", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.6.24", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3685b5c81fce630efc3e143a4ded235b107f1b1cdf186c3f115529e5e5ae4265" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.96", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519629a3f80976d89c575895b05677cbc45eaf9f70d62a364d819ba646409cc8" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.6.24", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -7085,27 +7793,27 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] name = "zerofrom" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", "synstructure", ] @@ -7126,7 +7834,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", ] [[package]] @@ -7148,7 +7856,50 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.87", + "syn 2.0.96", +] + +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "aes 0.8.4", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap 2.7.0", + "lzma-rs", + "memchr", + "pbkdf2", + "rand 0.8.5", + "sha1", + "thiserror 2.0.11", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] [[package]] @@ -7178,3 +7929,45 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zvariant" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e6b9b5f1361de2d5e7d9fd1ee5f6f7fcb6060618a1f82f3472f58f2b8d4be9" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "winnow 0.6.24", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573a8dd76961957108b10f7a45bac6ab1ea3e9b7fe01aff88325dc57bb8f5c8b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.96", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd46446ea2a1f353bfda53e35f17633afa79f4fe290a611c94645c69fe96a50" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.96", + "winnow 0.6.24", +] diff --git a/core/helpers/Cargo.toml b/core/helpers/Cargo.toml index 9af19018e..caf4b3eef 100644 --- a/core/helpers/Cargo.toml +++ b/core/helpers/Cargo.toml @@ -11,7 +11,7 @@ futures = "0.3.28" lazy_async_pool = "0.3.3" models = { path = "../models" } pin-project = "1.1.3" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" tokio = { version = "1", features = ["full"] } diff --git a/core/models/Cargo.toml b/core/models/Cargo.toml index 44295745d..9e216bc60 100644 --- a/core/models/Cargo.toml +++ b/core/models/Cargo.toml @@ -24,7 +24,8 @@ patch-db = { version = "*", path = "../../patch-db/patch-db", features = [ rand = "0.8.5" regex = "1.10.2" reqwest = "0.12" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } +rustls = "0.23" serde = { version = "1.0", features = ["derive", "rc"] } serde_json = "1.0" sqlx = { version = "0.7.2", features = [ @@ -39,3 +40,4 @@ tokio = { version = "1", features = ["full"] } torut = { git = "https://github.com/Start9Labs/torut.git", branch = "update/dependencies" } tracing = "0.1.39" yasi = "0.1.5" +zbus = "5" diff --git a/core/models/src/data_url.rs b/core/models/src/data_url.rs index 9757d7f6a..8a95c993e 100644 --- a/core/models/src/data_url.rs +++ b/core/models/src/data_url.rs @@ -168,6 +168,6 @@ fn doesnt_reallocate() { mime: InternedString::intern("png"), data: Cow::Borrowed(&random[..i]), }; - assert_eq!(dbg!(icon.to_string()).capacity(), icon.data_url_len()); + assert_eq!(icon.to_string().capacity(), icon.data_url_len()); } } diff --git a/core/models/src/errors.rs b/core/models/src/errors.rs index 2077a8bbd..21ff5072b 100644 --- a/core/models/src/errors.rs +++ b/core/models/src/errors.rs @@ -45,7 +45,7 @@ pub enum ErrorKind { ConfigGen = 27, ParseNumber = 28, Database = 29, - InvalidPackageId = 30, + InvalidId = 30, InvalidSignature = 31, Backup = 32, Restore = 33, @@ -90,6 +90,7 @@ pub enum ErrorKind { Lxc = 72, Cancelled = 73, Git = 74, + DBus = 75, } impl ErrorKind { pub fn as_str(&self) -> &'static str { @@ -124,7 +125,7 @@ impl ErrorKind { ConfigGen => "Config Generation Error", ParseNumber => "Number Parsing Error", Database => "Database Error", - InvalidPackageId => "Invalid Package ID", + InvalidId => "Invalid ID", InvalidSignature => "Invalid Signature", Backup => "Backup Error", Restore => "Restore Error", @@ -169,6 +170,7 @@ impl ErrorKind { Lxc => "LXC Error", Cancelled => "Cancelled", Git => "Git Error", + DBus => "DBus Error", } } } @@ -224,7 +226,7 @@ impl From for Error { } impl From for Error { fn from(err: InvalidId) -> Self { - Error::new(err, ErrorKind::InvalidPackageId) + Error::new(err, ErrorKind::InvalidId) } } impl From for Error { @@ -327,6 +329,16 @@ impl From for Error { Error::new(e, ErrorKind::Tor) } } +impl From for Error { + fn from(e: zbus::Error) -> Self { + Error::new(e, ErrorKind::DBus) + } +} +impl From for Error { + fn from(e: rustls::Error) -> Self { + Error::new(e, ErrorKind::OpenSsl) + } +} impl From for Error { fn from(value: patch_db::value::Error) -> Self { match value.kind { diff --git a/core/models/src/id/invalid_id.rs b/core/models/src/id/invalid_id.rs index d2cc82bd5..4a6f0f2e7 100644 --- a/core/models/src/id/invalid_id.rs +++ b/core/models/src/id/invalid_id.rs @@ -1,3 +1,5 @@ +use yasi::InternedString; + #[derive(Debug, thiserror::Error)] -#[error("Invalid ID")] -pub struct InvalidId; +#[error("Invalid ID: {0}")] +pub struct InvalidId(pub(super) InternedString); diff --git a/core/models/src/id/mod.rs b/core/models/src/id/mod.rs index 90dbc978c..0c313973f 100644 --- a/core/models/src/id/mod.rs +++ b/core/models/src/id/mod.rs @@ -43,7 +43,7 @@ impl TryFrom for Id { if ID_REGEX.is_match(&value) { Ok(Id(value)) } else { - Err(InvalidId) + Err(InvalidId(value)) } } } @@ -53,7 +53,7 @@ impl TryFrom for Id { if ID_REGEX.is_match(&value) { Ok(Id(InternedString::intern(value))) } else { - Err(InvalidId) + Err(InvalidId(InternedString::intern(value))) } } } @@ -63,7 +63,7 @@ impl TryFrom<&str> for Id { if ID_REGEX.is_match(value) { Ok(Id(InternedString::intern(value))) } else { - Err(InvalidId) + Err(InvalidId(InternedString::intern(value))) } } } diff --git a/core/startos/Cargo.toml b/core/startos/Cargo.toml index cba5bc6dc..df1622851 100644 --- a/core/startos/Cargo.toml +++ b/core/startos/Cargo.toml @@ -14,7 +14,7 @@ keywords = [ name = "start-os" readme = "README.md" repository = "https://github.com/Start9Labs/start-os" -version = "0.3.6-alpha.9" +version = "0.3.6-alpha.13" # VERSION_BUMP license = "MIT" [lib] @@ -50,7 +50,7 @@ test = [] [dependencies] aes = { version = "0.7.5", features = ["ctr"] } -async-acme = { version = "0.5.0", git = "https://github.com/dr-bonez/async-acme.git", features = [ +async-acme = { version = "0.6.0", git = "https://github.com/dr-bonez/async-acme.git", features = [ "use_rustls", "use_tokio", ] } @@ -62,7 +62,6 @@ async-compression = { version = "0.4.4", features = [ async-stream = "0.3.5" async-trait = "0.1.74" axum = { version = "0.7.3", features = ["ws"] } -axum-server = "0.6.0" barrage = "0.2.3" backhand = "0.18.0" base32 = "0.5.0" @@ -76,6 +75,7 @@ clap = "4.4.12" color-eyre = "0.6.2" console = "0.15.7" console-subscriber = { version = "0.3.0", optional = true } +const_format = "0.2.34" cookie = "0.18.0" cookie_store = "0.21.0" der = { version = "0.7.9", features = ["derive", "pem"] } @@ -102,18 +102,22 @@ 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", +hyper = { version = "1.5", features = ["server", "http1", "http2"] } +hyper-util = { version = "0.1.10", features = [ + "server", + "server-auto", + "server-graceful", "service", "http1", "http2", + "tokio", ] } id-pool = { version = "0.2.2", default-features = false, features = [ "serde", "u16", ] } imbl = "2.0.3" -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +imbl-value = "0.1.2" include_dir = { version = "0.7.3", features = ["metadata"] } indexmap = { version = "2.0.2", features = ["serde"] } indicatif = { version = "0.17.7", features = ["tokio"] } @@ -131,12 +135,14 @@ lazy_format = "2.0" lazy_static = "1.4.0" libc = "0.2.149" log = "0.4.20" +mio = "1" mbrman = "0.5.2" models = { version = "*", path = "../models" } new_mime_guess = "4" nix = { version = "0.29.0", features = [ "fs", "mount", + "net", "process", "sched", "signal", @@ -166,7 +172,7 @@ regex = "1.10.2" reqwest = { version = "0.12.4", features = ["stream", "json", "socks"] } reqwest_cookie_store = "0.8.0" rpassword = "7.2.0" -rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "refactor/no-dyn-ctx" } +rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", branch = "master" } rust-argon2 = "2.0.0" rustyline-async = "0.4.1" semver = { version = "1.0.20", features = ["serde"] } @@ -216,7 +222,11 @@ unix-named-pipe = "0.2.0" url = { version = "2.4.1", features = ["serde"] } urlencoding = "2.1.3" uuid = { version = "1.4.1", features = ["v4"] } +zbus = "5.1.1" zeroize = "1.6.0" +mail-send = { git = "https://github.com/dr-bonez/mail-send.git", branch = "main" } +rustls = "0.23.20" +rustls-pki-types = { version = "1.10.1", features = ["alloc"] } [profile.test] opt-level = 3 diff --git a/core/startos/src/account.rs b/core/startos/src/account.rs index 9e755342f..389589667 100644 --- a/core/startos/src/account.rs +++ b/core/startos/src/account.rs @@ -24,7 +24,7 @@ pub struct AccountInfo { pub server_id: String, pub hostname: Hostname, pub password: String, - pub tor_key: TorSecretKeyV3, + pub tor_keys: Vec, pub root_ca_key: PKey, pub root_ca_cert: X509, pub ssh_key: ssh_key::PrivateKey, @@ -34,7 +34,7 @@ impl AccountInfo { pub fn new(password: &str, start_time: SystemTime) -> Result { let server_id = generate_id(); let hostname = generate_hostname(); - let tor_key = TorSecretKeyV3::generate(); + let tor_key = vec![TorSecretKeyV3::generate()]; let root_ca_key = generate_key()?; let root_ca_cert = make_root_cert(&root_ca_key, &hostname, start_time)?; let ssh_key = ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::random( @@ -45,7 +45,7 @@ impl AccountInfo { server_id, hostname, password: hash_password(password)?, - tor_key, + tor_keys: tor_key, root_ca_key, root_ca_cert, ssh_key, @@ -58,8 +58,11 @@ impl AccountInfo { let hostname = Hostname(db.as_public().as_server_info().as_hostname().de()?); let password = db.as_private().as_password().de()?; let key_store = db.as_private().as_key_store(); - let tor_addr = db.as_public().as_server_info().as_onion_address().de()?; - let tor_key = key_store.as_onion().get_key(&tor_addr)?; + let tor_addrs = db.as_public().as_server_info().as_host().as_onions().de()?; + let tor_keys = tor_addrs + .into_iter() + .map(|tor_addr| key_store.as_onion().get_key(&tor_addr)) + .collect::>()?; let cert_store = key_store.as_local_certs(); let root_ca_key = cert_store.as_root_key().de()?.0; let root_ca_cert = cert_store.as_root_cert().de()?.0; @@ -70,7 +73,7 @@ impl AccountInfo { server_id, hostname, password, - tor_key, + tor_keys, root_ca_key, root_ca_cert, ssh_key, @@ -82,17 +85,16 @@ impl AccountInfo { let server_info = db.as_public_mut().as_server_info_mut(); server_info.as_id_mut().ser(&self.server_id)?; server_info.as_hostname_mut().ser(&self.hostname.0)?; - server_info - .as_lan_address_mut() - .ser(&self.hostname.lan_address().parse()?)?; server_info .as_pubkey_mut() .ser(&self.ssh_key.public_key().to_openssh()?)?; - let onion_address = self.tor_key.public().get_onion_address(); - server_info.as_onion_address_mut().ser(&onion_address)?; - server_info - .as_tor_address_mut() - .ser(&format!("https://{onion_address}").parse()?)?; + server_info.as_host_mut().as_onions_mut().ser( + &self + .tor_keys + .iter() + .map(|tor_key| tor_key.public().get_onion_address()) + .collect(), + )?; db.as_private_mut().as_password_mut().ser(&self.password)?; db.as_private_mut() .as_ssh_privkey_mut() @@ -101,7 +103,9 @@ impl AccountInfo { .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)?; + for tor_key in &self.tor_keys { + key_store.as_onion_mut().insert_key(tor_key)?; + } let cert_store = key_store.as_local_certs_mut(); cert_store .as_root_key_mut() diff --git a/core/startos/src/action.rs b/core/startos/src/action.rs index 801360a44..b29768d73 100644 --- a/core/startos/src/action.rs +++ b/core/startos/src/action.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use std::fmt; use clap::{CommandFactory, FromArgMatches, Parser}; diff --git a/core/startos/src/auth.rs b/core/startos/src/auth.rs index a6b624b70..9085709ab 100644 --- a/core/startos/src/auth.rs +++ b/core/startos/src/auth.rs @@ -187,9 +187,8 @@ pub fn check_password_against_db(db: &DatabaseModel, password: &str) -> Result<( Ok(()) } -#[derive(Deserialize, Serialize, Parser, TS)] +#[derive(Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] #[ts(export)] pub struct LoginParams { password: Option, diff --git a/core/startos/src/backup/os.rs b/core/startos/src/backup/os.rs index 6f08c5f43..324543fb8 100644 --- a/core/startos/src/backup/os.rs +++ b/core/startos/src/backup/os.rs @@ -85,7 +85,7 @@ impl OsBackupV0 { &mut rand::thread_rng(), ssh_key::Algorithm::Ed25519, )?, - tor_key: TorSecretKeyV3::from(self.tor_key.0), + tor_keys: vec![TorSecretKeyV3::from(self.tor_key.0)], compat_s9pk_key: ed25519_dalek::SigningKey::generate(&mut rand::thread_rng()), }, ui: self.ui, @@ -114,7 +114,7 @@ impl OsBackupV1 { root_ca_key: self.root_ca_key.0, 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)), + tor_keys: vec![TorSecretKeyV3::from(ed25519_expand_key(&self.net_key.0))], compat_s9pk_key: ed25519_dalek::SigningKey::from_bytes(&self.net_key), }, ui: self.ui, @@ -132,7 +132,7 @@ struct OsBackupV2 { 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 + tor_keys: Vec, // Base64 Encoded Ed25519 Expanded Secret Key compat_s9pk_key: Pem, // PEM Encoded ED25519 Key ui: Value, // JSON Value } @@ -146,7 +146,7 @@ impl OsBackupV2 { root_ca_key: self.root_ca_key.0, root_ca_cert: self.root_ca_cert.0, ssh_key: self.ssh_key.0, - tor_key: self.tor_key, + tor_keys: self.tor_keys, compat_s9pk_key: self.compat_s9pk_key.0, }, ui: self.ui, @@ -159,7 +159,7 @@ impl OsBackupV2 { root_ca_key: Pem(backup.account.root_ca_key.clone()), 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(), + tor_keys: backup.account.tor_keys.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 28d70653f..21f154b60 100644 --- a/core/startos/src/backup/restore.rs +++ b/core/startos/src/backup/restore.rs @@ -18,7 +18,7 @@ use crate::db::model::Database; use crate::disk::mount::backup::BackupMountGuard; use crate::disk::mount::filesystem::ReadWrite; use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard}; -use crate::init::{init, InitResult}; +use crate::init::init; use crate::prelude::*; use crate::s9pk::S9pk; use crate::service::service_map::DownloadInstallFuture; @@ -109,12 +109,13 @@ pub async fn recover_full_embassy( db.put(&ROOT, &Database::init(&os_backup.account)?).await?; drop(db); - let InitResult { net_ctrl } = init(&ctx.config, init_phases).await?; + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; let rpc_ctx = RpcContext::init( + &ctx.webserver, &ctx.config, disk_guid.clone(), - Some(net_ctrl), + Some(init_result), rpc_ctx_phases, ) .await?; diff --git a/core/startos/src/bins/container_cli.rs b/core/startos/src/bins/container_cli.rs index b0da1cb00..118133f55 100644 --- a/core/startos/src/bins/container_cli.rs +++ b/core/startos/src/bins/container_cli.rs @@ -4,7 +4,7 @@ use rpc_toolkit::CliApp; use serde_json::Value; use crate::service::cli::{ContainerCliContext, ContainerClientConfig}; -use crate::util::logger::EmbassyLogger; +use crate::util::logger::LOGGER; use crate::version::{Current, VersionT}; lazy_static::lazy_static! { @@ -12,7 +12,7 @@ lazy_static::lazy_static! { } pub fn main(args: impl IntoIterator) { - EmbassyLogger::init(); + LOGGER.enable(); if let Err(e) = CliApp::new( |cfg: ContainerClientConfig| Ok(ContainerCliContext::init(cfg)), crate::service::effects::handler(), diff --git a/core/startos/src/bins/registry.rs b/core/startos/src/bins/registry.rs index 132e0984a..a71b737af 100644 --- a/core/startos/src/bins/registry.rs +++ b/core/startos/src/bins/registry.rs @@ -5,16 +5,16 @@ use futures::FutureExt; use tokio::signal::unix::signal; use tracing::instrument; -use crate::net::web_server::WebServer; +use crate::net::web_server::{Acceptor, WebServer}; use crate::prelude::*; use crate::registry::context::{RegistryConfig, RegistryContext}; -use crate::util::logger::EmbassyLogger; +use crate::util::logger::LOGGER; #[instrument(skip_all)] async fn inner_main(config: &RegistryConfig) -> Result<(), Error> { let server = async { let ctx = RegistryContext::init(config).await?; - let mut server = WebServer::new(ctx.listen); + let mut server = WebServer::new(Acceptor::bind([ctx.listen]).await?); server.serve_registry(ctx.clone()); let mut shutdown_recv = ctx.shutdown.subscribe(); @@ -63,7 +63,7 @@ async fn inner_main(config: &RegistryConfig) -> Result<(), Error> { } pub fn main(args: impl IntoIterator) { - EmbassyLogger::init(); + LOGGER.enable(); let config = RegistryConfig::parse_from(args).load().unwrap(); diff --git a/core/startos/src/bins/start_cli.rs b/core/startos/src/bins/start_cli.rs index 2e92e0cc0..bda5e00d3 100644 --- a/core/startos/src/bins/start_cli.rs +++ b/core/startos/src/bins/start_cli.rs @@ -5,7 +5,7 @@ use serde_json::Value; use crate::context::config::ClientConfig; use crate::context::CliContext; -use crate::util::logger::EmbassyLogger; +use crate::util::logger::LOGGER; use crate::version::{Current, VersionT}; lazy_static::lazy_static! { @@ -13,7 +13,8 @@ lazy_static::lazy_static! { } pub fn main(args: impl IntoIterator) { - EmbassyLogger::init(); + LOGGER.enable(); + if let Err(e) = CliApp::new( |cfg: ClientConfig| Ok(CliContext::init(cfg.load()?)?), crate::expanded_api(), diff --git a/core/startos/src/bins/start_init.rs b/core/startos/src/bins/start_init.rs index 394d42c8d..fdd0e075d 100644 --- a/core/startos/src/bins/start_init.rs +++ b/core/startos/src/bins/start_init.rs @@ -1,3 +1,4 @@ +use std::path::Path; use std::sync::Arc; use tokio::process::Command; @@ -10,17 +11,17 @@ use crate::disk::fsck::RepairStrategy; use crate::disk::main::DEFAULT_PASSWORD; use crate::disk::REPAIR_DISK_PATH; use crate::firmware::{check_for_firmware_update, update_firmware}; -use crate::init::{InitPhases, InitResult, STANDBY_MODE_PATH}; -use crate::net::web_server::WebServer; +use crate::init::{InitPhases, STANDBY_MODE_PATH}; +use crate::net::web_server::{UpgradableListener, WebServer}; use crate::prelude::*; use crate::progress::FullProgressTracker; use crate::shutdown::Shutdown; use crate::util::Invoke; -use crate::PLATFORM; +use crate::{DATA_DIR, PLATFORM}; #[instrument(skip_all)] async fn setup_or_init( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { if let Some(firmware) = check_for_firmware_update() @@ -111,7 +112,7 @@ async fn setup_or_init( .await .is_err() { - let ctx = SetupContext::init(config)?; + let ctx = SetupContext::init(server, config)?; server.serve_setup(ctx.clone()); @@ -156,7 +157,7 @@ async fn setup_or_init( let disk_guid = Arc::new(String::from(guid_string.trim())); let requires_reboot = crate::disk::main::import( &**disk_guid, - config.datadir(), + DATA_DIR, if tokio::fs::metadata(REPAIR_DISK_PATH).await.is_ok() { RepairStrategy::Aggressive } else { @@ -178,18 +179,26 @@ async fn setup_or_init( tracing::info!("Loaded Disk"); if requires_reboot.0 { + tracing::info!("Rebooting..."); let mut reboot_phase = handle.add_phase("Rebooting".into(), Some(1)); reboot_phase.start(); return Ok(Err(Shutdown { - export_args: Some((disk_guid, config.datadir().to_owned())), + export_args: Some((disk_guid, Path::new(DATA_DIR).to_owned())), restart: true, })); } - let InitResult { net_ctrl } = crate::init::init(config, init_phases).await?; + let init_result = + crate::init::init(&server.acceptor_setter(), config, init_phases).await?; - let rpc_ctx = - RpcContext::init(config, disk_guid, Some(net_ctrl), rpc_ctx_phases).await?; + let rpc_ctx = RpcContext::init( + &server.acceptor_setter(), + config, + disk_guid, + Some(init_result), + rpc_ctx_phases, + ) + .await?; Ok::<_, Error>(Ok((rpc_ctx, handle))) } @@ -203,7 +212,7 @@ async fn setup_or_init( #[instrument(skip_all)] pub async fn main( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { if &*PLATFORM == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() { diff --git a/core/startos/src/bins/startd.rs b/core/startos/src/bins/startd.rs index d383f3091..b7574b7c7 100644 --- a/core/startos/src/bins/startd.rs +++ b/core/startos/src/bins/startd.rs @@ -1,6 +1,6 @@ use std::cmp::max; use std::ffi::OsString; -use std::net::{Ipv6Addr, SocketAddr}; +use std::net::IpAddr; use std::sync::Arc; use clap::Parser; @@ -12,21 +12,27 @@ use tracing::instrument; use crate::context::config::ServerConfig; use crate::context::rpc::InitRpcContextPhases; use crate::context::{DiagnosticContext, InitContext, RpcContext}; -use crate::net::web_server::WebServer; +use crate::net::network_interface::SelfContainedNetworkInterfaceListener; +use crate::net::utils::ipv6_is_local; +use crate::net::web_server::{Acceptor, UpgradableListener, WebServer}; use crate::shutdown::Shutdown; use crate::system::launch_metrics_task; -use crate::util::logger::EmbassyLogger; +use crate::util::io::append_file; +use crate::util::logger::LOGGER; use crate::{Error, ErrorKind, ResultExt}; #[instrument(skip_all)] async fn inner_main( - server: &mut WebServer, + server: &mut WebServer, config: &ServerConfig, ) -> Result, Error> { let rpc_ctx = if !tokio::fs::metadata("/run/startos/initialized") .await .is_ok() { + LOGGER.set_logfile(Some( + append_file("/run/startos/init.log").await?.into_std().await, + )); let (ctx, handle) = match super::start_init::main(server, &config).await? { Err(s) => return Ok(Some(s)), Ok(ctx) => ctx, @@ -34,6 +40,7 @@ async fn inner_main( tokio::fs::write("/run/startos/initialized", "").await?; server.serve_main(ctx.clone()); + LOGGER.set_logfile(None); handle.complete(); ctx @@ -44,6 +51,7 @@ async fn inner_main( server.serve_init(init_ctx); let ctx = RpcContext::init( + &server.acceptor_setter(), config, Arc::new( tokio::fs::read_to_string("/media/startos/config/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy @@ -131,7 +139,7 @@ async fn inner_main( } pub fn main(args: impl IntoIterator) { - EmbassyLogger::init(); + LOGGER.enable(); let config = ServerConfig::parse_from(args).load().unwrap(); @@ -142,7 +150,10 @@ pub fn main(args: impl IntoIterator) { .build() .expect("failed to initialize runtime"); rt.block_on(async { - let mut server = WebServer::new(SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80)); + let addrs = crate::net::utils::all_socket_addrs_for(80).await?; + let mut server = WebServer::new(Acceptor::bind_upgradable( + SelfContainedNetworkInterfaceListener::bind(80), + )); match inner_main(&mut server, &config).await { Ok(a) => { server.shutdown().await; diff --git a/core/startos/src/context/config.rs b/core/startos/src/context/config.rs index e02648919..4a0925d81 100644 --- a/core/startos/src/context/config.rs +++ b/core/startos/src/context/config.rs @@ -13,6 +13,7 @@ use crate::disk::OsPartitionInfo; use crate::init::init_postgres; use crate::prelude::*; use crate::util::serde::IoFormat; +use crate::MAIN_DATA; pub const DEVICE_CONFIG_PATH: &str = "/media/startos/config/config.yaml"; // "/media/startos/config/config.yaml"; pub const CONFIG_PATH: &str = "/etc/startos/config.yaml"; @@ -103,17 +104,11 @@ pub struct ServerConfig { #[arg(skip)] pub os_partitions: Option, #[arg(long)] - pub bind_rpc: Option, - #[arg(long)] pub tor_control: Option, #[arg(long)] pub tor_socks: Option, #[arg(long)] - pub dns_bind: Option>, - #[arg(long)] pub revision_cache_size: Option, - #[arg(short, long)] - pub datadir: Option, #[arg(long)] pub disable_encryption: Option, #[arg(long)] @@ -126,15 +121,12 @@ impl ContextConfig for ServerConfig { fn merge_with(&mut self, other: Self) { self.ethernet_interface = self.ethernet_interface.take().or(other.ethernet_interface); self.os_partitions = self.os_partitions.take().or(other.os_partitions); - self.bind_rpc = self.bind_rpc.take().or(other.bind_rpc); self.tor_control = self.tor_control.take().or(other.tor_control); self.tor_socks = self.tor_socks.take().or(other.tor_socks); - self.dns_bind = self.dns_bind.take().or(other.dns_bind); self.revision_cache_size = self .revision_cache_size .take() .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.multi_arch_s9pks = self.multi_arch_s9pks.take().or(other.multi_arch_s9pks); } @@ -148,13 +140,8 @@ impl ServerConfig { self.load_path_rec(Some(CONFIG_PATH))?; Ok(self) } - pub fn datadir(&self) -> &Path { - self.datadir - .as_deref() - .unwrap_or_else(|| Path::new("/embassy-data")) - } pub async fn db(&self) -> Result { - let db_path = self.datadir().join("main").join("embassy.db"); + let db_path = Path::new(MAIN_DATA).join("embassy.db"); let db = PatchDb::open(&db_path) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, db_path.display().to_string()))?; @@ -163,7 +150,7 @@ impl ServerConfig { } #[instrument(skip_all)] pub async fn secret_store(&self) -> Result { - init_postgres(self.datadir()).await?; + init_postgres("/media/startos/data").await?; let secret_store = PgPool::connect_with(PgConnectOptions::new().database("secrets").username("root")) .await?; diff --git a/core/startos/src/context/diagnostic.rs b/core/startos/src/context/diagnostic.rs index 0bf67e172..6acb21e30 100644 --- a/core/startos/src/context/diagnostic.rs +++ b/core/startos/src/context/diagnostic.rs @@ -1,5 +1,4 @@ use std::ops::Deref; -use std::path::PathBuf; use std::sync::Arc; use rpc_toolkit::yajrc::RpcError; @@ -13,7 +12,6 @@ use crate::shutdown::Shutdown; use crate::Error; pub struct DiagnosticContextSeed { - pub datadir: PathBuf, pub shutdown: Sender, pub error: Arc, pub disk_guid: Option>, @@ -25,7 +23,7 @@ pub struct DiagnosticContext(Arc); impl DiagnosticContext { #[instrument(skip_all)] pub fn init( - config: &ServerConfig, + _config: &ServerConfig, disk_guid: Option>, error: Error, ) -> Result { @@ -35,7 +33,6 @@ impl DiagnosticContext { let (shutdown, _) = tokio::sync::broadcast::channel(1); Ok(Self(Arc::new(DiagnosticContextSeed { - datadir: config.datadir().to_owned(), shutdown, disk_guid, error: Arc::new(error.into()), diff --git a/core/startos/src/context/rpc.rs b/core/startos/src/context/rpc.rs index 73d103adc..dfaa02b01 100644 --- a/core/startos/src/context/rpc.rs +++ b/core/startos/src/context/rpc.rs @@ -2,7 +2,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::future::Future; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::ops::Deref; -use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; @@ -27,10 +26,11 @@ use crate::auth::Sessions; use crate::context::config::ServerConfig; use crate::db::model::Database; use crate::disk::OsPartitionInfo; -use crate::init::check_time_is_synchronized; +use crate::init::{check_time_is_synchronized, InitResult}; use crate::lxc::{ContainerId, LxcContainer, LxcManager}; -use crate::net::net_controller::{NetController, PreInitNetController}; +use crate::net::net_controller::{NetController, NetService}; use crate::net::utils::{find_eth_iface, find_wifi_iface}; +use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter}; use crate::net::wifi::WpaCli; use crate::prelude::*; use crate::progress::{FullProgressTracker, PhaseProgressTrackerHandle}; @@ -47,13 +47,13 @@ pub struct RpcContextSeed { pub os_partitions: OsPartitionInfo, pub wifi_interface: Option, pub ethernet_interface: String, - pub datadir: PathBuf, pub disk_guid: Arc, pub ephemeral_sessions: SyncMutex, pub db: TypedPatchDb, pub sync_db: watch::Sender, pub account: RwLock, pub net_controller: Arc, + pub os_net_service: NetService, pub s9pk_arch: Option<&'static str>, pub services: ServiceMap, pub metrics_cache: RwLock>, @@ -85,7 +85,7 @@ pub struct InitRpcContextPhases { load_db: PhaseProgressTrackerHandle, init_net_ctrl: PhaseProgressTrackerHandle, cleanup_init: CleanupInitPhases, - // TODO: migrations + run_migrations: PhaseProgressTrackerHandle, } impl InitRpcContextPhases { pub fn new(handle: &FullProgressTracker) -> Self { @@ -93,6 +93,7 @@ impl InitRpcContextPhases { load_db: handle.add_phase("Loading database".into(), Some(5)), init_net_ctrl: handle.add_phase("Initializing network".into(), Some(1)), cleanup_init: CleanupInitPhases::new(handle), + run_migrations: handle.add_phase("Running migrations".into(), Some(10)), } } } @@ -117,13 +118,15 @@ pub struct RpcContext(Arc); impl RpcContext { #[instrument(skip_all)] pub async fn init( + webserver: &WebServerAcceptorSetter, config: &ServerConfig, disk_guid: Arc, - net_ctrl: Option, + init_result: Option, InitRpcContextPhases { mut load_db, mut init_net_ctrl, cleanup_init, + run_migrations, }: InitRpcContextPhases, ) -> Result { let tor_proxy = config.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( @@ -133,7 +136,7 @@ impl RpcContext { let (shutdown, _) = tokio::sync::broadcast::channel(1); load_db.start(); - let db = if let Some(net_ctrl) = &net_ctrl { + let db = if let Some(InitResult { net_ctrl, .. }) = &init_result { net_ctrl.db.clone() } else { TypedPatchDb::::load(config.db().await?).await? @@ -144,29 +147,28 @@ impl RpcContext { tracing::info!("Opened PatchDB"); init_net_ctrl.start(); - let net_controller = Arc::new( - NetController::init( - if let Some(net_ctrl) = net_ctrl { - net_ctrl - } else { - PreInitNetController::init( - db.clone(), - config - .tor_control - .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), - tor_proxy, - &account.hostname, - account.tor_key.clone(), - ) - .await? - }, - config - .dns_bind - .as_deref() - .unwrap_or(&[SocketAddr::from(([127, 0, 0, 1], 53))]), - ) - .await?, - ); + let (net_controller, os_net_service) = if let Some(InitResult { + net_ctrl, + os_net_service, + }) = init_result + { + (net_ctrl, os_net_service) + } else { + let net_ctrl = Arc::new( + NetController::init( + db.clone(), + config + .tor_control + .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), + tor_proxy, + &account.hostname, + ) + .await?, + ); + webserver.try_upgrade(|a| net_ctrl.net_iface.upgrade_listener(a))?; + let os_net_service = net_ctrl.os_bindings().await?; + (net_ctrl, os_net_service) + }; init_net_ctrl.complete(); tracing::info!("Initialized Net Controller"); @@ -210,7 +212,6 @@ impl RpcContext { let seed = Arc::new(RpcContextSeed { is_closed: AtomicBool::new(false), - datadir: config.datadir().to_path_buf(), os_partitions: config.os_partitions.clone().ok_or_else(|| { Error::new( eyre!("OS Partition Information Missing"), @@ -229,6 +230,7 @@ impl RpcContext { db, account: RwLock::new(account), net_controller, + os_net_service, s9pk_arch: if config.multi_arch_s9pks.unwrap_or(false) { None } else { @@ -276,7 +278,9 @@ impl RpcContext { let res = Self(seed.clone()); res.cleanup_and_initialize(cleanup_init).await?; tracing::info!("Cleaned up transient states"); - crate::version::post_init(&res).await?; + + crate::version::post_init(&res, run_migrations).await?; + tracing::info!("Completed migrations"); Ok(res) } @@ -286,7 +290,6 @@ impl RpcContext { self.services.shutdown_all().await?; self.is_closed.store(true, Ordering::SeqCst); tracing::info!("RPC Context is shutdown"); - // TODO: shutdown http servers Ok(()) } diff --git a/core/startos/src/context/setup.rs b/core/startos/src/context/setup.rs index 96ec07700..b8e4ee968 100644 --- a/core/startos/src/context/setup.rs +++ b/core/startos/src/context/setup.rs @@ -1,5 +1,5 @@ use std::ops::Deref; -use std::path::PathBuf; +use std::path::Path; use std::sync::Arc; use std::time::Duration; @@ -10,8 +10,6 @@ use josekit::jwk::Jwk; use patch_db::PatchDb; use rpc_toolkit::Context; use serde::{Deserialize, Serialize}; -use sqlx::postgres::PgConnectOptions; -use sqlx::PgPool; use tokio::sync::broadcast::Sender; use tokio::sync::OnceCell; use tracing::instrument; @@ -22,12 +20,13 @@ use crate::context::config::ServerConfig; use crate::context::RpcContext; use crate::disk::OsPartitionInfo; use crate::hostname::Hostname; -use crate::init::init_postgres; +use crate::net::web_server::{UpgradableListener, WebServer, WebServerAcceptorSetter}; use crate::prelude::*; use crate::progress::FullProgressTracker; use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations}; use crate::setup::SetupProgress; use crate::util::net::WebSocketExt; +use crate::MAIN_DATA; lazy_static::lazy_static! { pub static ref CURRENT_SECRET: Jwk = Jwk::generate_ec_key(josekit::jwk::alg::ec::EcCurve::P256).unwrap_or_else(|e| { @@ -41,7 +40,7 @@ lazy_static::lazy_static! { #[serde(rename_all = "camelCase")] #[ts(export)] pub struct SetupResult { - pub tor_address: String, + pub tor_addresses: Vec, #[ts(type = "string")] pub hostname: Hostname, #[ts(type = "string")] @@ -52,7 +51,11 @@ impl TryFrom<&AccountInfo> for SetupResult { type Error = Error; fn try_from(value: &AccountInfo) -> Result { Ok(Self { - tor_address: format!("https://{}", value.tor_key.public().get_onion_address()), + tor_addresses: value + .tor_keys + .iter() + .map(|tor_key| format!("https://{}", tor_key.public().get_onion_address())) + .collect(), hostname: value.hostname.clone(), lan_address: value.hostname.lan_address(), root_ca: String::from_utf8(value.root_ca_cert.to_pem()?)?, @@ -61,6 +64,7 @@ impl TryFrom<&AccountInfo> for SetupResult { } pub struct SetupContextSeed { + pub webserver: WebServerAcceptorSetter, pub config: ServerConfig, pub os_partitions: OsPartitionInfo, pub disable_encryption: bool, @@ -68,7 +72,6 @@ pub struct SetupContextSeed { pub task: OnceCell>, pub result: OnceCell>, pub shutdown: Sender<()>, - pub datadir: PathBuf, pub rpc_continuations: RpcContinuations, } @@ -76,10 +79,13 @@ pub struct SetupContextSeed { pub struct SetupContext(Arc); impl SetupContext { #[instrument(skip_all)] - pub fn init(config: &ServerConfig) -> Result { + pub fn init( + webserver: &WebServer, + config: &ServerConfig, + ) -> Result { let (shutdown, _) = tokio::sync::broadcast::channel(1); - let datadir = config.datadir().to_owned(); Ok(Self(Arc::new(SetupContextSeed { + webserver: webserver.acceptor_setter(), config: config.clone(), os_partitions: config.os_partitions.clone().ok_or_else(|| { Error::new( @@ -92,13 +98,12 @@ impl SetupContext { task: OnceCell::new(), result: OnceCell::new(), shutdown, - datadir, rpc_continuations: RpcContinuations::new(), }))) } #[instrument(skip_all)] pub async fn db(&self) -> Result { - let db_path = self.datadir.join("main").join("embassy.db"); + let db_path = Path::new(MAIN_DATA).join("embassy.db"); let db = PatchDb::open(&db_path) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, db_path.display().to_string()))?; @@ -161,21 +166,30 @@ impl SetupContext { if let Err(e) = async { let mut stream = progress_tracker.stream(Some(Duration::from_millis(100))); - while let Some(progress) = stream.next().await { - ws.send(ws::Message::Text( - serde_json::to_string(&progress) - .with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; - if progress.overall.is_complete() { - break; + loop { + tokio::select! { + progress = stream.next() => { + if let Some(progress) = progress { + ws.send(ws::Message::Text( + serde_json::to_string(&progress) + .with_kind(ErrorKind::Serialization)?, + )) + .await + .with_kind(ErrorKind::Network)?; + if progress.overall.is_complete() { + return ws.normal_close("complete").await; + } + } else { + return ws.normal_close("complete").await; + } + } + msg = ws.recv() => { + if msg.transpose().with_kind(ErrorKind::Network)?.is_none() { + return Ok(()) + } + } } } - - ws.normal_close("complete").await?; - - Ok::<_, Error>(()) } .await { diff --git a/core/startos/src/db/mod.rs b/core/startos/src/db/mod.rs index c7e38e81c..135153935 100644 --- a/core/startos/src/db/mod.rs +++ b/core/startos/src/db/mod.rs @@ -198,17 +198,26 @@ pub async fn subscribe( session, |mut ws| async move { if let Err(e) = async { - while let Some(rev) = sub.recv().await { - ws.send(ws::Message::Text( - serde_json::to_string(&rev).with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; + loop { + tokio::select! { + rev = sub.recv() => { + if let Some(rev) = rev { + ws.send(ws::Message::Text( + serde_json::to_string(&rev).with_kind(ErrorKind::Serialization)?, + )) + .await + .with_kind(ErrorKind::Network)?; + } else { + return ws.normal_close("complete").await; + } + } + msg = ws.recv() => { + if msg.transpose().with_kind(ErrorKind::Network)?.is_none() { + return Ok(()) + } + } + } } - - ws.normal_close("complete").await?; - - Ok::<_, Error>(()) } .await { diff --git a/core/startos/src/db/model/public.rs b/core/startos/src/db/model/public.rs index 85978134d..1f764e0c9 100644 --- a/core/startos/src/db/model/public.rs +++ b/core/startos/src/db/model/public.rs @@ -1,28 +1,31 @@ use std::collections::{BTreeMap, BTreeSet}; -use std::net::{Ipv4Addr, Ipv6Addr}; +use std::net::{IpAddr, Ipv4Addr}; use chrono::{DateTime, Utc}; use exver::{Version, VersionRange}; use imbl_value::InternedString; -use ipnet::{Ipv4Net, Ipv6Net}; +use ipnet::IpNet; use isocountry::CountryCode; use itertools::Itertools; use models::PackageId; use openssl::hash::MessageDigest; use patch_db::{HasModel, Value}; -use reqwest::Url; use serde::{Deserialize, Serialize}; -use torut::onion::OnionAddressV3; use ts_rs::TS; use crate::account::AccountInfo; use crate::db::model::package::AllPackageData; -use crate::net::utils::{get_iface_ipv4_addr, get_iface_ipv6_addr}; +use crate::net::acme::AcmeProvider; +use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo}; +use crate::net::host::Host; +use crate::net::utils::ipv6_is_local; +use crate::net::vhost::AlpnInfo; use crate::prelude::*; use crate::progress::FullProgress; use crate::system::SmtpValue; use crate::util::cpupower::Governor; use crate::util::lshw::LshwDevice; +use crate::util::serde::MaybeUtf8String; use crate::version::{Current, VersionT}; use crate::{ARCH, PLATFORM}; @@ -38,7 +41,6 @@ pub struct Public { } impl Public { pub fn init(account: &AccountInfo) -> Result { - let lan_address = account.hostname.lan_address().parse().unwrap(); Ok(Self { server_info: ServerInfo { arch: get_arch(), @@ -46,16 +48,44 @@ impl Public { id: account.server_id.clone(), version: Current::default().semver(), hostname: account.hostname.no_dot_host_name(), + host: Host { + bindings: [( + 80, + BindInfo { + enabled: false, + options: BindOptions { + preferred_external_port: 80, + add_ssl: Some(AddSslOptions { + preferred_external_port: 443, + alpn: Some(AlpnInfo::Specified(vec![ + MaybeUtf8String("http/1.1".into()), + MaybeUtf8String("h2".into()), + ])), + }), + secure: None, + }, + net: NetInfo { + assigned_port: None, + assigned_ssl_port: Some(443), + public: false, + }, + }, + )] + .into_iter() + .collect(), + onions: account + .tor_keys + .iter() + .map(|k| k.public().get_onion_address()) + .collect(), + domains: BTreeMap::new(), + hostname_info: BTreeMap::new(), + }, last_backup: None, package_version_compat: Current::default().compat().clone(), post_init_migration_todos: BTreeSet::new(), - lan_address, - onion_address: account.tor_key.public().get_onion_address(), - tor_address: format!("https://{}", account.tor_key.public().get_onion_address()) - .parse() - .unwrap(), - ip_info: BTreeMap::new(), - acme: None, + network_interfaces: BTreeMap::new(), + acme: BTreeMap::new(), status_info: ServerStatus { backup_progress: None, updated: false, @@ -115,6 +145,7 @@ pub struct ServerInfo { pub id: String, #[ts(type = "string")] pub hostname: InternedString, + pub host: Host, #[ts(type = "string")] pub version: Version, #[ts(type = "string")] @@ -123,15 +154,11 @@ pub struct ServerInfo { pub post_init_migration_todos: BTreeSet, #[ts(type = "string | null")] pub last_backup: Option>, - #[ts(type = "string")] - pub lan_address: Url, - #[ts(type = "string")] - pub onion_address: OnionAddressV3, - /// for backwards compatibility - #[ts(type = "string")] - pub tor_address: Url, - pub ip_info: BTreeMap, - pub acme: Option, + #[ts(as = "BTreeMap::")] + #[serde(default)] + pub network_interfaces: BTreeMap, + #[serde(default)] + pub acme: BTreeMap, #[serde(default)] pub status_info: ServerStatus, pub wifi: WifiInfo, @@ -151,43 +178,76 @@ pub struct ServerInfo { pub devices: Vec, } -#[derive(Debug, Deserialize, Serialize, HasModel, TS)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] #[ts(export)] -pub struct IpInfo { - #[ts(type = "string | null")] - pub ipv4_range: Option, - pub ipv4: Option, - #[ts(type = "string | null")] - pub ipv6_range: Option, - pub ipv6: Option, +pub struct NetworkInterfaceInfo { + pub public: Option, + pub ip_info: Option, } -impl IpInfo { - pub async fn for_interface(iface: &str) -> Result { - let (ipv4, ipv4_range) = get_iface_ipv4_addr(iface).await?.unzip(); - let (ipv6, ipv6_range) = get_iface_ipv6_addr(iface).await?.unzip(); - Ok(Self { - ipv4_range, - ipv4, - ipv6_range, - ipv6, +impl NetworkInterfaceInfo { + pub fn public(&self) -> bool { + self.public.unwrap_or_else(|| { + !self.ip_info.as_ref().map_or(true, |ip_info| { + let ip4s = ip_info + .subnets + .iter() + .filter_map(|ipnet| { + if let IpAddr::V4(ip4) = ipnet.addr() { + Some(ip4) + } else { + None + } + }) + .collect::>(); + if !ip4s.is_empty() { + return ip4s.iter().all(|ip4| { + ip4.is_loopback() + || (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations + || ip4.is_link_local() + }); + } + ip_info.subnets.iter().all(|ipnet| { + if let IpAddr::V6(ip6) = ipnet.addr() { + ipv6_is_local(ip6) + } else { + true + } + }) + }) }) } } +#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct IpInfo { + pub scope_id: u32, + pub device_type: Option, + #[ts(type = "string[]")] + pub subnets: BTreeSet, + pub wan_ip: Option, + #[ts(type = "string[]")] + pub ntp_servers: BTreeSet, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, TS)] +#[ts(export)] +#[serde(rename_all = "kebab-case")] +pub enum NetworkInterfaceType { + Ethernet, + Wireless, + Wireguard, +} + #[derive(Debug, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] #[ts(export)] pub struct AcmeSettings { - #[ts(type = "string")] - pub provider: Url, - /// email addresses for letsencrypt pub contact: Vec, - #[ts(type = "string[]")] - /// domains to get letsencrypt certs for - pub domains: BTreeSet, } #[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] diff --git a/core/startos/src/diagnostic.rs b/core/startos/src/diagnostic.rs index f0c142706..73ef93125 100644 --- a/core/startos/src/diagnostic.rs +++ b/core/startos/src/diagnostic.rs @@ -10,7 +10,7 @@ use crate::context::{CliContext, DiagnosticContext, RpcContext}; use crate::init::SYSTEM_REBUILD_PATH; use crate::shutdown::Shutdown; use crate::util::io::delete_file; -use crate::Error; +use crate::{Error, DATA_DIR}; pub fn diagnostic() -> ParentHandler { ParentHandler::new() @@ -71,7 +71,7 @@ pub fn restart(ctx: DiagnosticContext) -> Result<(), Error> { export_args: ctx .disk_guid .clone() - .map(|guid| (guid, ctx.datadir.clone())), + .map(|guid| (guid, Path::new(DATA_DIR).to_owned())), restart: true, }) .expect("receiver dropped"); diff --git a/core/startos/src/disk/mount/backup.rs b/core/startos/src/disk/mount/backup.rs index 2c322c284..3c6f97d91 100644 --- a/core/startos/src/disk/mount/backup.rs +++ b/core/startos/src/disk/mount/backup.rs @@ -7,7 +7,6 @@ use models::PackageId; use tokio::io::AsyncWriteExt; use tracing::instrument; -use super::filesystem::ecryptfs::EcryptFS; use super::guard::{GenericMountGuard, TmpMountGuard}; use crate::auth::check_password; use crate::backup::target::BackupInfo; diff --git a/core/startos/src/disk/mount/filesystem/mod.rs b/core/startos/src/disk/mount/filesystem/mod.rs index 818549a0a..80bfcc903 100644 --- a/core/startos/src/disk/mount/filesystem/mod.rs +++ b/core/startos/src/disk/mount/filesystem/mod.rs @@ -1,7 +1,6 @@ use std::ffi::OsStr; use std::fmt::{Display, Write}; use std::path::Path; -use std::time::Duration; use digest::generic_array::GenericArray; use digest::OutputSizeUser; diff --git a/core/startos/src/init.rs b/core/startos/src/init.rs index a81e7e336..63642dfb3 100644 --- a/core/startos/src/init.rs +++ b/core/startos/src/init.rs @@ -3,10 +3,12 @@ use std::io::Cursor; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::os::unix::fs::PermissionsExt; use std::path::Path; +use std::sync::Arc; use std::time::{Duration, SystemTime}; use axum::extract::ws::{self}; use color_eyre::eyre::eyre; +use const_format::formatcp; use futures::{StreamExt, TryStreamExt}; use itertools::Itertools; use models::ResultExt; @@ -23,21 +25,24 @@ use crate::context::{CliContext, InitContext}; use crate::db::model::public::ServerStatus; use crate::db::model::Database; use crate::disk::mount::util::unmount; +use crate::hostname::Hostname; use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH; -use crate::net::net_controller::PreInitNetController; +use crate::net::net_controller::{NetController, NetService}; +use crate::net::utils::find_wifi_iface; +use crate::net::web_server::{UpgradableListener, WebServerAcceptorSetter}; use crate::prelude::*; use crate::progress::{ FullProgress, FullProgressTracker, PhaseProgressTrackerHandle, PhasedProgressBar, }; use crate::rpc_continuations::{Guid, RpcContinuation}; use crate::s9pk::v2::pack::{CONTAINER_DATADIR, CONTAINER_TOOL}; -use crate::ssh::SSH_AUTHORIZED_KEYS_FILE; +use crate::ssh::SSH_DIR; use crate::system::get_mem_info; use crate::util::io::{create_file, IOHook}; use crate::util::lshw::lshw; use crate::util::net::WebSocketExt; use crate::util::{cpupower, Invoke}; -use crate::Error; +use crate::{Error, MAIN_DATA, PACKAGE_DATA}; pub const SYSTEM_REBUILD_PATH: &str = "/media/startos/config/system-rebuild"; pub const STANDBY_MODE_PATH: &str = "/media/startos/config/standby"; @@ -195,7 +200,8 @@ pub async fn init_postgres(datadir: impl AsRef) -> Result<(), Error> { } pub struct InitResult { - pub net_ctrl: PreInitNetController, + pub net_ctrl: Arc, + pub os_net_service: NetService, } pub struct InitPhases { @@ -213,7 +219,6 @@ pub struct InitPhases { enable_zram: PhaseProgressTrackerHandle, update_server_info: PhaseProgressTrackerHandle, launch_service_network: PhaseProgressTrackerHandle, - run_migrations: PhaseProgressTrackerHandle, validate_db: PhaseProgressTrackerHandle, postinit: Option, } @@ -238,7 +243,6 @@ impl InitPhases { enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)), update_server_info: handle.add_phase("Updating server info".into(), Some(1)), launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)), - run_migrations: handle.add_phase("Running migrations".into(), Some(10)), validate_db: handle.add_phase("Validating database".into(), Some(1)), postinit: if Path::new("/media/startos/config/postinit.sh").exists() { Some(handle.add_phase("Running postinit.sh".into(), Some(5))) @@ -274,6 +278,7 @@ pub async fn run_script>(path: P, mut progress: PhaseProgressTrac #[instrument(skip_all)] pub async fn init( + webserver: &WebServerAcceptorSetter, cfg: &ServerConfig, InitPhases { preinit, @@ -290,7 +295,6 @@ pub async fn init( mut enable_zram, mut update_server_info, mut launch_service_network, - run_migrations, mut validate_db, postinit, }: InitPhases, @@ -317,7 +321,7 @@ pub async fn init( })?; tokio::fs::set_permissions(LOCAL_AUTH_COOKIE_PATH, Permissions::from_mode(0o046)).await?; Command::new("chown") - .arg("root:embassy") + .arg("root:startos") .arg(LOCAL_AUTH_COOKIE_PATH) .invoke(crate::ErrorKind::Filesystem) .await?; @@ -334,8 +338,10 @@ pub async fn init( load_ssh_keys.start(); crate::ssh::sync_keys( + &Hostname(peek.as_public().as_server_info().as_hostname().de()?), + &peek.as_private().as_ssh_privkey().de()?, &peek.as_private().as_ssh_pubkeys().de()?, - SSH_AUTHORIZED_KEYS_FILE, + SSH_DIR, ) .await?; load_ssh_keys.complete(); @@ -344,22 +350,25 @@ pub async fn init( let account = AccountInfo::load(&peek)?; start_net.start(); - let net_ctrl = PreInitNetController::init( - db.clone(), - cfg.tor_control - .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), - cfg.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new(127, 0, 0, 1), - 9050, - ))), - &account.hostname, - account.tor_key, - ) - .await?; + let net_ctrl = Arc::new( + NetController::init( + db.clone(), + cfg.tor_control + .unwrap_or(SocketAddr::from(([127, 0, 0, 1], 9051))), + cfg.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new(127, 0, 0, 1), + 9050, + ))), + &account.hostname, + ) + .await?, + ); + webserver.try_upgrade(|a| net_ctrl.net_iface.upgrade_listener(a))?; + let os_net_service = net_ctrl.os_bindings().await?; start_net.complete(); mount_logs.start(); - let log_dir = cfg.datadir().join("main/logs"); + let log_dir = Path::new(MAIN_DATA).join("logs"); if tokio::fs::metadata(&log_dir).await.is_err() { tokio::fs::create_dir_all(&log_dir).await?; } @@ -390,8 +399,6 @@ pub async fn init( mount_logs.complete(); tracing::info!("Mounted Logs"); - let mut server_info = peek.as_public().as_server_info().de()?; - load_ca_cert.start(); // write to ca cert store tokio::fs::write( @@ -402,58 +409,46 @@ pub async fn init( Command::new("update-ca-certificates") .invoke(crate::ErrorKind::OpenSsl) .await?; - if tokio::fs::metadata("/home/kiosk/profile").await.is_ok() { - Command::new("certutil") - .arg("-A") - .arg("-n") - .arg("StartOS Local Root CA") - .arg("-t") - .arg("TCu,Cuw,Tuw") - .arg("-i") - .arg("/usr/local/share/ca-certificates/startos-root-ca.crt") - .arg("-d") - .arg("/home/kiosk/fx-profile") - .invoke(ErrorKind::OpenSsl) - .await?; - } load_ca_cert.complete(); load_wifi.start(); - crate::net::wifi::synchronize_wpa_supplicant_conf( - &cfg.datadir().join("main"), - &mut server_info.wifi, - ) - .await?; + let wifi_interface = find_wifi_iface().await?; + let wifi = db + .mutate(|db| { + let wifi = db.as_public_mut().as_server_info_mut().as_wifi_mut(); + wifi.as_interface_mut().ser(&wifi_interface)?; + wifi.de() + }) + .await?; + crate::net::wifi::synchronize_network_manager(MAIN_DATA, &wifi).await?; load_wifi.complete(); tracing::info!("Synchronized WiFi"); init_tmp.start(); - let tmp_dir = cfg.datadir().join("package-data/tmp"); + let tmp_dir = Path::new(PACKAGE_DATA).join("tmp"); if tokio::fs::metadata(&tmp_dir).await.is_ok() { tokio::fs::remove_dir_all(&tmp_dir).await?; } if tokio::fs::metadata(&tmp_dir).await.is_err() { tokio::fs::create_dir_all(&tmp_dir).await?; } - let tmp_var = cfg.datadir().join(format!("package-data/tmp/var")); + let tmp_var = Path::new(PACKAGE_DATA).join("tmp/var"); if tokio::fs::metadata(&tmp_var).await.is_ok() { tokio::fs::remove_dir_all(&tmp_var).await?; } crate::disk::mount::util::bind(&tmp_var, "/var/tmp", false).await?; - let downloading = cfg - .datadir() - .join(format!("package-data/archive/downloading")); + let downloading = Path::new(PACKAGE_DATA).join("archive/downloading"); if tokio::fs::metadata(&downloading).await.is_ok() { tokio::fs::remove_dir_all(&downloading).await?; } - let tmp_docker = cfg - .datadir() - .join(format!("package-data/tmp/{CONTAINER_TOOL}")); + let tmp_docker = Path::new(PACKAGE_DATA).join(formatcp!("tmp/{CONTAINER_TOOL}")); crate::disk::mount::util::bind(&tmp_docker, CONTAINER_DATADIR, false).await?; init_tmp.complete(); + let server_info = db.peek().await.into_public().into_server_info(); set_governor.start(); - let governor = if let Some(governor) = &server_info.governor { + let selected_governor = server_info.as_governor().de()?; + let governor = if let Some(governor) = &selected_governor { if cpupower::get_available_governors() .await? .contains(governor) @@ -474,11 +469,11 @@ pub async fn init( set_governor.complete(); sync_clock.start(); - server_info.ntp_synced = false; + let mut ntp_synced = false; let mut not_made_progress = 0u32; for _ in 0..1800 { if check_time_is_synchronized().await? { - server_info.ntp_synced = true; + ntp_synced = true; break; } let t = SystemTime::now(); @@ -495,7 +490,7 @@ pub async fn init( break; } } - if !server_info.ntp_synced { + if !ntp_synced { tracing::warn!("Timed out waiting for system time to synchronize"); } else { tracing::info!("Syncronized system clock"); @@ -503,16 +498,16 @@ pub async fn init( sync_clock.complete(); enable_zram.start(); - if server_info.zram { - crate::system::enable_zram().await? + if server_info.as_zram().de()? { + crate::system::enable_zram().await?; + tracing::info!("Enabled ZRAM"); } enable_zram.complete(); update_server_info.start(); - server_info.ip_info = crate::net::dhcp::init_ips().await?; - server_info.ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024; - server_info.devices = lshw().await?; - server_info.status_info = ServerStatus { + let ram = get_mem_info().await?.total.0 as u64 * 1024 * 1024; + let devices = lshw().await?; + let status_info = ServerStatus { updated: false, update_progress: None, backup_progress: None, @@ -520,10 +515,15 @@ pub async fn init( restarting: false, }; db.mutate(|v| { - v.as_public_mut().as_server_info_mut().ser(&server_info)?; + let server_info = v.as_public_mut().as_server_info_mut(); + server_info.as_ntp_synced_mut().ser(&ntp_synced)?; + server_info.as_ram_mut().ser(&ram)?; + server_info.as_devices_mut().ser(&devices)?; + server_info.as_status_info_mut().ser(&status_info)?; Ok(()) }) .await?; + tracing::info!("Updated server info"); update_server_info.complete(); launch_service_network.start(); @@ -532,6 +532,7 @@ pub async fn init( .arg("lxc-net.service") .invoke(ErrorKind::Lxc) .await?; + tracing::info!("Launched service intranet"); launch_service_network.complete(); validate_db.start(); @@ -540,6 +541,7 @@ pub async fn init( d.ser(&model) }) .await?; + tracing::info!("Validated database"); validate_db.complete(); if let Some(progress) = postinit { @@ -548,7 +550,10 @@ pub async fn init( tracing::info!("System initialized."); - Ok(InitResult { net_ctrl }) + Ok(InitResult { + net_ctrl, + os_net_service, + }) } pub fn init_api() -> ParentHandler { diff --git a/core/startos/src/install/mod.rs b/core/startos/src/install/mod.rs index f153c88ad..33c2ea6c7 100644 --- a/core/startos/src/install/mod.rs +++ b/core/startos/src/install/mod.rs @@ -2,6 +2,7 @@ use std::ops::Deref; use std::path::PathBuf; use std::time::Duration; +use axum::extract::ws; use clap::builder::ValueParserFactory; use clap::{value_parser, CommandFactory, FromArgMatches, Parser}; use color_eyre::eyre::eyre; @@ -12,7 +13,7 @@ use itertools::Itertools; use models::{FromStrParser, VersionString}; use reqwest::header::{HeaderMap, CONTENT_LENGTH}; use reqwest::Url; -use rpc_toolkit::yajrc::{GenericRpcMethod, RpcError}; +use rpc_toolkit::yajrc::RpcError; use rpc_toolkit::HandlerArgs; use rustyline_async::ReadlineEvent; use serde::{Deserialize, Serialize}; @@ -188,7 +189,7 @@ pub async fn sideload( SideloadParams { session }: SideloadParams, ) -> Result { let (upload, file) = upload(&ctx, session.clone()).await?; - let (err_send, err_recv) = oneshot::channel::(); + let (err_send, mut err_recv) = oneshot::channel::(); let progress = Guid::new(); let progress_tracker = FullProgressTracker::new(); let mut progress_listener = progress_tracker.stream(Some(Duration::from_millis(200))); @@ -198,43 +199,44 @@ pub async fn sideload( RpcContinuation::ws_authed( &ctx, session, - |mut ws| { - use axum::extract::ws::Message; - async move { - if let Err(e) = async { - type RpcResponse = rpc_toolkit::yajrc::RpcResponse< - GenericRpcMethod<&'static str, (), FullProgress>, - >; + |mut ws| async move { + if let Err(e) = async { + loop { tokio::select! { - res = async { - while let Some(progress) = progress_listener.next().await { - ws.send(Message::Text( + progress = progress_listener.next() => { + if let Some(progress) = progress { + ws.send(ws::Message::Text( serde_json::to_string(&progress) .with_kind(ErrorKind::Serialization)?, )) .await .with_kind(ErrorKind::Network)?; + if progress.overall.is_complete() { + return ws.normal_close("complete").await; + } + } else { + return ws.normal_close("complete").await; } - Ok::<_, Error>(()) - } => res?, - err = err_recv => { + } + msg = ws.recv() => { + if msg.transpose().with_kind(ErrorKind::Network)?.is_none() { + return Ok(()) + } + } + err = (&mut err_recv) => { if let Ok(e) = err { ws.close_result(Err::<&str, _>(e.clone_output())).await?; return Err(e) } } } - - ws.normal_close("complete").await?; - - Ok::<_, Error>(()) - } - .await - { - tracing::error!("Error tracking sideload progress: {e}"); - tracing::debug!("{e:?}"); } } + .await + { + tracing::error!("Error tracking sideload progress: {e}"); + tracing::debug!("{e:?}"); + } }, Duration::from_secs(600), ), @@ -258,9 +260,9 @@ pub async fn sideload( } .await { - let _ = err_send.send(e.clone_output()); tracing::error!("Error sideloading package: {e}"); tracing::debug!("{e:?}"); + let _ = err_send.send(e); } }); Ok(SideloadResponse { upload, progress }) diff --git a/core/startos/src/lib.rs b/core/startos/src/lib.rs index 3c5875e36..bcdcb7f91 100644 --- a/core/startos/src/lib.rs +++ b/core/startos/src/lib.rs @@ -1,6 +1,11 @@ +use const_format::formatcp; + +pub const DATA_DIR: &str = "/media/startos/data"; +pub const MAIN_DATA: &str = formatcp!("{DATA_DIR}/main"); +pub const PACKAGE_DATA: &str = formatcp!("{DATA_DIR}/package-data"); 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 const HOST_IP: [u8; 4] = [10, 0, 3, 1]; pub use std::env::consts::ARCH; lazy_static::lazy_static! { pub static ref PLATFORM: String = { @@ -82,6 +87,7 @@ use crate::context::{ CliContext, DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext, }; use crate::disk::fsck::RequiresReboot; +use crate::net::net; use crate::registry::context::{RegistryContext, RegistryUrlParams}; use crate::util::serde::HandlerExtSerde; @@ -295,13 +301,20 @@ pub fn server() -> ParentHandler { .with_about("Set system smtp server and credentials") .with_call_remote::() ) + .subcommand( + "test-smtp", + from_fn_async(system::test_smtp) + .no_display() + .with_about("Send test email using provided smtp server and credentials") + .with_call_remote::() + ) .subcommand( "clear-smtp", from_fn_async(system::clear_system_smtp) .no_display() .with_about("Remove system smtp server and credentials") .with_call_remote::() - ) + ).subcommand("host", net::host::server_host_api::().with_about("Commands for modifying the host for the system ui")) } pub fn package() -> ParentHandler { @@ -415,7 +428,7 @@ pub fn package() -> ParentHandler { .subcommand("attach", from_fn_async(service::cli_attach).no_display()) .subcommand( "host", - net::host::host::().with_about("Manage network hosts for a package"), + net::host::host_api::().with_about("Manage network hosts for a package"), ) } diff --git a/core/startos/src/logs.rs b/core/startos/src/logs.rs index 1cb3327b6..be5e9cb8f 100644 --- a/core/startos/src/logs.rs +++ b/core/startos/src/logs.rs @@ -30,6 +30,7 @@ use crate::error::ResultExt; use crate::lxc::ContainerId; use crate::prelude::*; use crate::rpc_continuations::{Guid, RpcContinuation, RpcContinuations}; +use crate::util::net::WebSocketExt; use crate::util::serde::Reversible; use crate::util::Invoke; @@ -80,34 +81,28 @@ async fn ws_handler( .with_kind(ErrorKind::Network)?; } - let mut ws_closed = false; - while let Some(entry) = tokio::select! { - a = logs.try_next() => Some(a?), - a = stream.try_next() => { a.with_kind(crate::ErrorKind::Network)?; ws_closed = true; None } - } { - if let Some(entry) = entry { - let (_, log_entry) = entry.log_entry()?; - stream - .send(ws::Message::Text( - serde_json::to_string(&log_entry).with_kind(ErrorKind::Serialization)?, - )) - .await - .with_kind(ErrorKind::Network)?; + loop { + tokio::select! { + entry = logs.try_next() => { + if let Some(entry) = entry? { + let (_, log_entry) = entry.log_entry()?; + stream + .send(ws::Message::Text( + serde_json::to_string(&log_entry).with_kind(ErrorKind::Serialization)?, + )) + .await + .with_kind(ErrorKind::Network)?; + } else { + return stream.normal_close("complete").await; + } + }, + msg = stream.try_next() => { + if msg.with_kind(crate::ErrorKind::Network)?.is_none() { + return Ok(()) + } + } } } - - if !ws_closed { - stream - .send(ws::Message::Close(Some(ws::CloseFrame { - code: ws::close_code::NORMAL, - reason: "Log Stream Finished".into(), - }))) - .await - .with_kind(ErrorKind::Network)?; - drop(stream); - } - - Ok(()) } #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] diff --git a/core/startos/src/lxc/dev.rs b/core/startos/src/lxc/dev.rs index 248546d88..a918672da 100644 --- a/core/startos/src/lxc/dev.rs +++ b/core/startos/src/lxc/dev.rs @@ -8,13 +8,11 @@ use rpc_toolkit::{ 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; -use crate::{ - context::{CliContext, RpcContext}, - service::ServiceStats, -}; +use crate::service::ServiceStats; pub fn lxc() -> ParentHandler { ParentHandler::new() diff --git a/core/startos/src/lxc/mod.rs b/core/startos/src/lxc/mod.rs index c0fb6eaba..ce5d28970 100644 --- a/core/startos/src/lxc/mod.rs +++ b/core/startos/src/lxc/mod.rs @@ -1,8 +1,8 @@ +use std::collections::BTreeSet; use std::net::Ipv4Addr; use std::path::Path; use std::sync::{Arc, Weak}; use std::time::Duration; -use std::{collections::BTreeSet, ffi::OsString}; use clap::builder::ValueParserFactory; use futures::{AsyncWriteExt, StreamExt}; diff --git a/core/startos/src/middleware/cors.rs b/core/startos/src/middleware/cors.rs index a8c406a8a..60e8247ea 100644 --- a/core/startos/src/middleware/cors.rs +++ b/core/startos/src/middleware/cors.rs @@ -1,6 +1,7 @@ +use axum::body::Body; use axum::extract::Request; use axum::response::Response; -use http::{HeaderMap, HeaderValue}; +use http::{HeaderMap, HeaderValue, Method}; use rpc_toolkit::{Empty, Middleware}; #[derive(Clone)] @@ -52,6 +53,13 @@ impl Middleware for Cors { request: &mut Request, ) -> Result<(), Response> { self.get_cors_headers(request); + if request.method() == Method::OPTIONS { + let mut response = Response::new(Body::empty()); + response + .headers_mut() + .extend(std::mem::take(&mut self.headers)); + return Err(response); + } Ok(()) } async fn process_http_response(&mut self, _: &Context, response: &mut Response) { diff --git a/core/startos/src/net/acme.rs b/core/startos/src/net/acme.rs index 95f9d4adb..9ef2af1f7 100644 --- a/core/startos/src/net/acme.rs +++ b/core/startos/src/net/acme.rs @@ -1,6 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::str::FromStr; +use async_acme::acme::Identifier; use clap::builder::ValueParserFactory; use clap::Parser; use imbl_value::InternedString; @@ -10,6 +11,7 @@ use openssl::pkey::{PKey, Private}; use openssl::x509::X509; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; +use ts_rs::TS; use url::Url; use crate::context::{CliContext, RpcContext}; @@ -78,10 +80,18 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { async fn read_certificate( &self, - domains: &[String], + identifiers: &[Identifier], directory_url: &str, ) -> Result, Self::Error> { - let domains = JsonKey::new(domains.into_iter().map(InternedString::intern).collect()); + let identifiers = JsonKey::new( + identifiers + .into_iter() + .map(|d| match d { + Identifier::Dns(d) => d.into(), + Identifier::Ip(ip) => InternedString::from_display(ip), + }) + .collect(), + ); let directory_url = directory_url .parse::() .with_kind(ErrorKind::ParseUrl)?; @@ -94,7 +104,7 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { .into_acme() .into_certs() .into_idx(&directory_url) - .and_then(|a| a.into_idx(&domains)) + .and_then(|a| a.into_idx(&identifiers)) else { return Ok(None); }; @@ -120,13 +130,21 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { async fn write_certificate( &self, - domains: &[String], + identifiers: &[Identifier], directory_url: &str, key_pem: &str, certificate_pem: &str, ) -> Result<(), Self::Error> { - tracing::info!("Saving new certificate for {domains:?}"); - let domains = JsonKey::new(domains.into_iter().map(InternedString::intern).collect()); + tracing::info!("Saving new certificate for {identifiers:?}"); + let identifiers = JsonKey::new( + identifiers + .into_iter() + .map(|d| match d { + Identifier::Dns(d) => d.into(), + Identifier::Ip(ip) => InternedString::from_display(ip), + }) + .collect(), + ); let directory_url = directory_url .parse::() .with_kind(ErrorKind::ParseUrl)?; @@ -146,7 +164,7 @@ impl<'a> async_acme::cache::AcmeCache for AcmeCertCache<'a> { .as_acme_mut() .as_certs_mut() .upsert(&directory_url, || Ok(BTreeMap::new()))? - .insert(&domains, &cert) + .insert(&identifiers, &cert) }) .await?; @@ -159,18 +177,23 @@ pub fn acme() -> ParentHandler { .subcommand( "init", from_fn_async(init) + .with_metadata("sync_db", Value::Bool(true)) .no_display() .with_about("Setup ACME certificate acquisition") .with_call_remote::(), ) .subcommand( - "domain", - domain::() - .with_about("Add, remove, or view domains for which to acquire ACME certificates"), + "remove", + from_fn_async(remove) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Setup ACME certificate acquisition") + .with_call_remote::(), ) } -#[derive(Clone, Deserialize, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, TS)] +#[ts(type = "string")] pub struct AcmeProvider(pub Url); impl FromStr for AcmeProvider { type Err = ::Err; @@ -180,9 +203,36 @@ impl FromStr for AcmeProvider { "letsencrypt-staging" => async_acme::acme::LETS_ENCRYPT_STAGING_DIRECTORY.parse(), s => s.parse(), } + .map(|mut u: Url| { + let path = u + .path_segments() + .into_iter() + .flatten() + .filter(|p| !p.is_empty()) + .map(|p| p.to_owned()) + .collect::>(); + if let Ok(mut path_mut) = u.path_segments_mut() { + path_mut.clear(); + path_mut.extend(path); + } + u + }) .map(Self) } } +impl<'de> Deserialize<'de> for AcmeProvider { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + crate::util::serde::deserialize_from_str(deserializer) + } +} +impl AsRef for AcmeProvider { + fn as_ref(&self) -> &str { + self.0.as_str() + } +} impl ValueParserFactory for AcmeProvider { type Parser = FromStrParser; fn value_parser() -> Self::Parser { @@ -200,125 +250,36 @@ pub struct InitAcmeParams { pub async fn init( ctx: RpcContext, - InitAcmeParams { - provider: AcmeProvider(provider), - contact, - }: InitAcmeParams, + InitAcmeParams { provider, contact }: InitAcmeParams, ) -> Result<(), Error> { ctx.db .mutate(|db| { db.as_public_mut() .as_server_info_mut() .as_acme_mut() - .map_mutate(|acme| { - Ok(Some(AcmeSettings { - provider, - contact, - domains: acme.map(|acme| acme.domains).unwrap_or_default(), - })) - }) + .insert(&provider, &AcmeSettings { contact }) }) .await?; Ok(()) } -pub fn domain() -> ParentHandler { - ParentHandler::new() - .subcommand( - "add", - from_fn_async(add_domain) - .no_display() - .with_about("Add a domain for which to acquire ACME certificates") - .with_call_remote::(), - ) - .subcommand( - "remove", - from_fn_async(remove_domain) - .no_display() - .with_about("Remove a domain for which to acquire ACME certificates") - .with_call_remote::(), - ) - .subcommand( - "list", - from_fn_async(list_domains) - .with_custom_display_fn(|_, res| { - for domain in res { - println!("{domain}") - } - Ok(()) - }) - .with_about("List domains for which to acquire ACME certificates") - .with_call_remote::(), - ) -} - #[derive(Deserialize, Serialize, Parser)] -pub struct DomainParams { - pub domain: InternedString, +pub struct RemoveAcmeParams { + #[arg(long)] + pub provider: AcmeProvider, } -pub async fn add_domain( +pub async fn remove( ctx: RpcContext, - DomainParams { domain }: DomainParams, + RemoveAcmeParams { provider }: RemoveAcmeParams, ) -> Result<(), Error> { ctx.db .mutate(|db| { db.as_public_mut() .as_server_info_mut() .as_acme_mut() - .transpose_mut() - .ok_or_else(|| { - Error::new( - eyre!("Please call `start-cli net acme init` before adding a domain"), - ErrorKind::InvalidRequest, - ) - })? - .as_domains_mut() - .mutate(|domains| { - domains.insert(domain); - Ok(()) - }) + .remove(&provider) }) .await?; Ok(()) } - -pub async fn remove_domain( - ctx: RpcContext, - DomainParams { domain }: DomainParams, -) -> Result<(), Error> { - ctx.db - .mutate(|db| { - if let Some(acme) = db - .as_public_mut() - .as_server_info_mut() - .as_acme_mut() - .transpose_mut() - { - acme.as_domains_mut().mutate(|domains| { - domains.remove(&domain); - Ok(()) - }) - } else { - Ok(()) - } - }) - .await?; - Ok(()) -} - -pub async fn list_domains(ctx: RpcContext) -> Result, Error> { - if let Some(acme) = ctx - .db - .peek() - .await - .into_public() - .into_server_info() - .into_acme() - .transpose() - { - acme.into_domains().de() - } else { - Ok(BTreeSet::new()) - } -} diff --git a/core/startos/src/net/dhcp.rs b/core/startos/src/net/dhcp.rs deleted file mode 100644 index e323ba371..000000000 --- a/core/startos/src/net/dhcp.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet}; -use std::net::IpAddr; - -use clap::Parser; -use futures::TryStreamExt; -use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler}; -use serde::{Deserialize, Serialize}; -use tokio::sync::RwLock; -use ts_rs::TS; - -use crate::context::{CliContext, RpcContext}; -use crate::db::model::public::IpInfo; -use crate::net::utils::{iface_is_physical, list_interfaces}; -use crate::prelude::*; -use crate::Error; - -lazy_static::lazy_static! { - static ref CACHED_IPS: RwLock> = RwLock::new(BTreeSet::new()); -} - -async fn _ips() -> Result, Error> { - Ok(init_ips() - .await? - .values() - .flat_map(|i| { - std::iter::empty() - .chain(i.ipv4.map(IpAddr::from)) - .chain(i.ipv6.map(IpAddr::from)) - }) - .collect()) -} - -pub async fn ips() -> Result, Error> { - let ips = CACHED_IPS.read().await.clone(); - if !ips.is_empty() { - return Ok(ips); - } - let ips = _ips().await?; - *CACHED_IPS.write().await = ips.clone(); - Ok(ips) -} - -pub async fn init_ips() -> Result, Error> { - let mut res = BTreeMap::new(); - let mut ifaces = list_interfaces(); - while let Some(iface) = ifaces.try_next().await? { - if iface_is_physical(&iface).await { - let ip_info = IpInfo::for_interface(&iface).await?; - res.insert(iface, ip_info); - } - } - Ok(res) -} - -// #[command(subcommands(update))] -pub fn dhcp() -> ParentHandler { - ParentHandler::new().subcommand( - "update", - from_fn_async::<_, _, (), Error, (RpcContext, UpdateParams)>(update) - .no_display() - .with_about("Update IP assigned by dhcp") - .with_call_remote::(), - ) -} -#[derive(Deserialize, Serialize, Parser, TS)] -#[serde(rename_all = "camelCase")] -#[command(rename_all = "kebab-case")] -pub struct UpdateParams { - interface: String, -} - -pub async fn update( - ctx: RpcContext, - UpdateParams { interface }: UpdateParams, -) -> Result<(), Error> { - if iface_is_physical(&interface).await { - let ip_info = IpInfo::for_interface(&interface).await?; - ctx.db - .mutate(|db| { - db.as_public_mut() - .as_server_info_mut() - .as_ip_info_mut() - .insert(&interface, &ip_info) - }) - .await?; - - let mut cached = CACHED_IPS.write().await; - if cached.is_empty() { - *cached = _ips().await?; - } else { - cached.extend( - std::iter::empty() - .chain(ip_info.ipv4.map(IpAddr::from)) - .chain(ip_info.ipv6.map(IpAddr::from)), - ); - } - } - Ok(()) -} diff --git a/core/startos/src/net/dns.rs b/core/startos/src/net/dns.rs index 016e5de9f..d68a971f1 100644 --- a/core/startos/src/net/dns.rs +++ b/core/startos/src/net/dns.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; use std::collections::BTreeMap; -use std::net::{Ipv4Addr, SocketAddr}; +use std::net::Ipv4Addr; use std::sync::{Arc, Weak}; use std::time::Duration; @@ -19,6 +19,7 @@ use trust_dns_server::server::{Request, RequestHandler, ResponseHandler, Respons use trust_dns_server::ServerFuture; use crate::net::forward::START9_BRIDGE_IFACE; +use crate::util::sync::Watch; use crate::util::Invoke; use crate::{Error, ErrorKind, ResultExt}; @@ -140,38 +141,46 @@ impl RequestHandler for Resolver { impl DnsController { #[instrument(skip_all)] - pub async fn init(bind: &[SocketAddr]) -> Result { + pub async fn init(mut lxcbr_status: Watch) -> Result { let services = Arc::new(RwLock::new(BTreeMap::new())); let mut server = ServerFuture::new(Resolver { services: services.clone(), }); - server.register_listener( - TcpListener::bind(bind) - .await - .with_kind(ErrorKind::Network)?, - Duration::from_secs(30), - ); - server.register_socket(UdpSocket::bind(bind).await.with_kind(ErrorKind::Network)?); - Command::new("resolvectl") - .arg("dns") - .arg(START9_BRIDGE_IFACE) - .arg("127.0.0.1") - .invoke(ErrorKind::Network) - .await?; - Command::new("resolvectl") - .arg("domain") - .arg(START9_BRIDGE_IFACE) - .arg("embassy") - .invoke(ErrorKind::Network) - .await?; + let dns_server = tokio::spawn(async move { + server.register_listener( + TcpListener::bind((Ipv4Addr::LOCALHOST, 53)) + .await + .with_kind(ErrorKind::Network)?, + Duration::from_secs(30), + ); + server.register_socket( + UdpSocket::bind((Ipv4Addr::LOCALHOST, 53)) + .await + .with_kind(ErrorKind::Network)?, + ); + + lxcbr_status.wait_for(|a| *a).await; + + Command::new("resolvectl") + .arg("dns") + .arg(START9_BRIDGE_IFACE) + .arg("127.0.0.1") + .invoke(ErrorKind::Network) + .await?; + Command::new("resolvectl") + .arg("domain") + .arg(START9_BRIDGE_IFACE) + .arg("embassy") + .invoke(ErrorKind::Network) + .await?; - let dns_server = tokio::spawn( server .block_until_done() - .map_err(|e| Error::new(e, ErrorKind::Network)), - ) + .await + .map_err(|e| Error::new(e, ErrorKind::Network)) + }) .into(); Ok(Self { diff --git a/core/startos/src/net/forward.rs b/core/startos/src/net/forward.rs index e954bc36a..6f3abee15 100644 --- a/core/startos/src/net/forward.rs +++ b/core/startos/src/net/forward.rs @@ -1,13 +1,18 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::net::SocketAddr; use std::sync::{Arc, Weak}; +use futures::channel::oneshot; +use helpers::NonDetachingJoinHandle; use id_pool::IdPool; +use imbl_value::InternedString; use serde::{Deserialize, Serialize}; use tokio::process::Command; -use tokio::sync::Mutex; +use tokio::sync::mpsc; +use crate::db::model::public::NetworkInterfaceInfo; use crate::prelude::*; +use crate::util::sync::Watch; use crate::util::Invoke; pub const START9_BRIDGE_IFACE: &str = "lxcbr0"; @@ -34,144 +39,269 @@ impl AvailablePorts { } } +#[derive(Debug)] +struct ForwardRequest { + public: bool, + target: SocketAddr, + rc: Weak<()>, +} + +#[derive(Debug, Default)] +struct ForwardState { + requested: BTreeMap, + current: BTreeMap>, +} +impl ForwardState { + async fn sync(&mut self, interfaces: &BTreeMap) -> Result<(), Error> { + let private_interfaces = interfaces + .iter() + .filter(|(_, public)| !*public) + .map(|(i, _)| i) + .collect::>(); + let all_interfaces = interfaces.keys().collect::>(); + self.requested.retain(|_, req| req.rc.strong_count() > 0); + for external in self + .requested + .keys() + .chain(self.current.keys()) + .copied() + .collect::>() + { + match ( + self.requested.get(&external), + self.current.get_mut(&external), + ) { + (Some(req), Some(cur)) => { + let expected = if req.public { + &all_interfaces + } else { + &private_interfaces + }; + let actual = cur.keys().collect::>(); + let mut to_rm = actual + .difference(expected) + .copied() + .cloned() + .collect::>(); + let mut to_add = expected + .difference(&actual) + .copied() + .cloned() + .collect::>(); + for interface in actual.intersection(expected).copied() { + if cur[interface] != req.target { + to_rm.insert(interface.clone()); + to_add.insert(interface.clone()); + } + } + for interface in to_rm { + unforward(external, &*interface, cur[&interface]).await?; + cur.remove(&interface); + } + for interface in to_add { + forward(external, &*interface, req.target).await?; + cur.insert(interface, req.target); + } + } + (Some(req), None) => { + let cur = self.current.entry(external).or_default(); + for interface in if req.public { + &all_interfaces + } else { + &private_interfaces + } + .into_iter() + .copied() + .cloned() + { + forward(external, &*interface, req.target).await?; + cur.insert(interface, req.target); + } + } + (None, Some(cur)) => { + let to_rm = cur.keys().cloned().collect::>(); + for interface in to_rm { + unforward(external, &*interface, cur[&interface]).await?; + cur.remove(&interface); + } + self.current.remove(&external); + } + _ => (), + } + } + Ok(()) + } +} + +fn err_has_exited(_: T) -> Error { + Error::new( + eyre!("PortForwardController thread has exited"), + ErrorKind::Unknown, + ) +} + pub struct LanPortForwardController { - forwards: Mutex>>>, + req: mpsc::UnboundedSender<( + Option<(u16, ForwardRequest)>, + oneshot::Sender>, + )>, + _thread: NonDetachingJoinHandle<()>, } impl LanPortForwardController { - pub fn new() -> Self { + pub fn new(mut ip_info: Watch>) -> Self { + let (req_send, mut req_recv) = mpsc::unbounded_channel(); + let thread = NonDetachingJoinHandle::from(tokio::spawn(async move { + let mut state = ForwardState::default(); + let mut interfaces = ip_info.peek_and_mark_seen(|ip_info| { + ip_info + .iter() + .map(|(iface, info)| (iface.clone(), info.public())) + .collect() + }); + let mut reply: Option>> = None; + loop { + tokio::select! { + msg = req_recv.recv() => { + if let Some((msg, re)) = msg { + if let Some((external, req)) = msg { + state.requested.insert(external, req); + } + reply = Some(re); + } else { + break; + } + } + _ = ip_info.changed() => { + interfaces = ip_info.peek(|ip_info| { + ip_info + .iter() + .map(|(iface, info)| (iface.clone(), info.public())) + .collect() + }); + } + } + let res = state.sync(&interfaces).await; + if let Err(e) = &res { + tracing::error!("Error in PortForwardController: {e}"); + tracing::debug!("{e:?}"); + } + if let Some(re) = reply.take() { + let _ = re.send(res); + } + } + })); Self { - forwards: Mutex::new(BTreeMap::new()), + req: req_send, + _thread: thread, } } - pub async fn add(&self, port: u16, addr: SocketAddr) -> Result, Error> { - let mut writable = self.forwards.lock().await; - let (prev, mut forward) = if let Some(forward) = writable.remove(&port) { - ( - forward.keys().next().cloned(), - forward - .into_iter() - .filter(|(_, rc)| rc.strong_count() > 0) - .collect(), - ) - } else { - (None, BTreeMap::new()) - }; + pub async fn add(&self, port: u16, public: bool, target: SocketAddr) -> Result, Error> { let rc = Arc::new(()); - forward.insert(addr, Arc::downgrade(&rc)); - let next = forward.keys().next().cloned(); - if !forward.is_empty() { - writable.insert(port, forward); - } + let (send, recv) = oneshot::channel(); + self.req + .send(( + Some(( + port, + ForwardRequest { + public, + target, + rc: Arc::downgrade(&rc), + }, + )), + send, + )) + .map_err(err_has_exited)?; - update_forward(port, prev, next).await?; - Ok(rc) + recv.await.map_err(err_has_exited)?.map(|_| rc) } - pub async fn gc(&self, external: u16) -> Result<(), Error> { - let mut writable = self.forwards.lock().await; - let (prev, forward) = if let Some(forward) = writable.remove(&external) { - ( - forward.keys().next().cloned(), - forward - .into_iter() - .filter(|(_, rc)| rc.strong_count() > 0) - .collect(), - ) - } else { - (None, BTreeMap::new()) - }; - let next = forward.keys().next().cloned(); - if !forward.is_empty() { - writable.insert(external, forward); - } + pub async fn gc(&self) -> Result<(), Error> { + let (send, recv) = oneshot::channel(); + self.req.send((None, send)).map_err(err_has_exited)?; - update_forward(external, prev, next).await + recv.await.map_err(err_has_exited)? } } -async fn update_forward( - external: u16, - prev: Option, - next: Option, -) -> Result<(), Error> { - if prev != next { - if let Some(prev) = prev { - unforward(START9_BRIDGE_IFACE, external, prev).await?; - } - if let Some(next) = next { - forward(START9_BRIDGE_IFACE, external, next).await?; - } - } - Ok(()) -} - // iptables -I FORWARD -o br-start9 -p tcp -d 172.18.0.2 --dport 8333 -j ACCEPT // iptables -t nat -I PREROUTING -p tcp --dport 32768 -j DNAT --to 172.18.0.2:8333 -async fn forward(iface: &str, external: u16, addr: SocketAddr) -> Result<(), Error> { - Command::new("iptables") - .arg("-I") - .arg("FORWARD") - .arg("-o") - .arg(iface) - .arg("-p") - .arg("tcp") - .arg("-d") - .arg(addr.ip().to_string()) - .arg("--dport") - .arg(addr.port().to_string()) - .arg("-j") - .arg("ACCEPT") - .invoke(crate::ErrorKind::Network) - .await?; - Command::new("iptables") - .arg("-t") - .arg("nat") - .arg("-I") - .arg("PREROUTING") - .arg("-p") - .arg("tcp") - .arg("--dport") - .arg(external.to_string()) - .arg("-j") - .arg("DNAT") - .arg("--to") - .arg(addr.to_string()) - .invoke(crate::ErrorKind::Network) - .await?; +async fn forward(external: u16, interface: &str, target: SocketAddr) -> Result<(), Error> { + for proto in ["tcp", "udp"] { + Command::new("iptables") + .arg("-I") + .arg("FORWARD") + .arg("-i") + .arg(interface) + .arg("-o") + .arg(START9_BRIDGE_IFACE) + .arg("-p") + .arg(proto) + .arg("-d") + .arg(target.ip().to_string()) + .arg("--dport") + .arg(target.port().to_string()) + .arg("-j") + .arg("ACCEPT") + .invoke(crate::ErrorKind::Network) + .await?; + Command::new("iptables") + .arg("-t") + .arg("nat") + .arg("-I") + .arg("PREROUTING") + .arg("-i") + .arg(interface) + .arg("-p") + .arg(proto) + .arg("--dport") + .arg(external.to_string()) + .arg("-j") + .arg("DNAT") + .arg("--to") + .arg(target.to_string()) + .invoke(crate::ErrorKind::Network) + .await?; + } Ok(()) } // iptables -D FORWARD -o br-start9 -p tcp -d 172.18.0.2 --dport 8333 -j ACCEPT // iptables -t nat -D PREROUTING -p tcp --dport 32768 -j DNAT --to 172.18.0.2:8333 -async fn unforward(iface: &str, external: u16, addr: SocketAddr) -> Result<(), Error> { - Command::new("iptables") - .arg("-D") - .arg("FORWARD") - .arg("-o") - .arg(iface) - .arg("-p") - .arg("tcp") - .arg("-d") - .arg(addr.ip().to_string()) - .arg("--dport") - .arg(addr.port().to_string()) - .arg("-j") - .arg("ACCEPT") - .invoke(crate::ErrorKind::Network) - .await?; - Command::new("iptables") - .arg("-t") - .arg("nat") - .arg("-D") - .arg("PREROUTING") - .arg("-p") - .arg("tcp") - .arg("--dport") - .arg(external.to_string()) - .arg("-j") - .arg("DNAT") - .arg("--to") - .arg(addr.to_string()) - .invoke(crate::ErrorKind::Network) - .await?; +async fn unforward(external: u16, interface: &str, target: SocketAddr) -> Result<(), Error> { + for proto in ["tcp", "udp"] { + Command::new("iptables") + .arg("-D") + .arg("FORWARD") + .arg("-i") + .arg(interface) + .arg("-o") + .arg(START9_BRIDGE_IFACE) + .arg("-p") + .arg(proto) + .arg("-d") + .arg(target.ip().to_string()) + .arg("--dport") + .arg(target.port().to_string()) + .arg("-j") + .arg("ACCEPT") + .invoke(crate::ErrorKind::Network) + .await?; + Command::new("iptables") + .arg("-t") + .arg("nat") + .arg("-D") + .arg("PREROUTING") + .arg("-i") + .arg(interface) + .arg("-p") + .arg(proto) + .arg("--dport") + .arg(external.to_string()) + .arg("-j") + .arg("DNAT") + .arg("--to") + .arg(target.to_string()) + .invoke(crate::ErrorKind::Network) + .await?; + } Ok(()) } diff --git a/core/startos/src/net/host/address.rs b/core/startos/src/net/host/address.rs index 05942ffa9..b75734a13 100644 --- a/core/startos/src/net/host/address.rs +++ b/core/startos/src/net/host/address.rs @@ -1,17 +1,22 @@ -use std::fmt; -use std::str::FromStr; +use std::collections::BTreeSet; -use clap::builder::ValueParserFactory; +use clap::Parser; use imbl_value::InternedString; -use models::FromStrParser; +use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use torut::onion::OnionAddressV3; use ts_rs::TS; +use crate::context::{CliContext, RpcContext}; +use crate::db::model::DatabaseModel; +use crate::net::acme::AcmeProvider; +use crate::net::host::{all_hosts, HostApiKind}; use crate::prelude::*; +use crate::util::serde::{display_serializable, HandlerExtSerde}; -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, TS)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Debug, Deserialize, Serialize, TS)] +#[serde(rename_all = "kebab-case")] +#[serde(rename_all_fields = "camelCase")] #[serde(tag = "kind")] #[ts(export)] pub enum HostAddress { @@ -22,36 +27,278 @@ pub enum HostAddress { Domain { #[ts(type = "string")] address: InternedString, + public: bool, + acme: Option, }, } -impl FromStr for HostAddress { - type Err = Error; - fn from_str(s: &str) -> Result { - if let Some(addr) = s.strip_suffix(".onion") { - Ok(HostAddress::Onion { - address: addr - .parse::() - .with_kind(ErrorKind::ParseUrl)?, - }) - } else { - Ok(HostAddress::Domain { address: s.into() }) - } - } +#[derive(Debug, Deserialize, Serialize, TS)] +pub struct DomainConfig { + pub public: bool, + pub acme: Option, } -impl fmt::Display for HostAddress { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Onion { address } => write!(f, "{address}"), - Self::Domain { address } => write!(f, "{address}"), +fn check_duplicates(db: &DatabaseModel) -> Result<(), Error> { + let mut onions = BTreeSet::::new(); + let mut domains = BTreeSet::::new(); + let mut check_onion = |onion: OnionAddressV3| { + if onions.contains(&onion) { + return Err(Error::new( + eyre!("onion address {onion} is already in use"), + ErrorKind::InvalidRequest, + )); + } + onions.insert(onion); + Ok(()) + }; + let mut check_domain = |domain: InternedString| { + if domains.contains(&domain) { + return Err(Error::new( + eyre!("domain {domain} is already in use"), + ErrorKind::InvalidRequest, + )); + } + domains.insert(domain); + Ok(()) + }; + for host in all_hosts(db) { + let host = host?; + for onion in host.as_onions().de()? { + check_onion(onion)?; + } + for domain in host.as_domains().keys()? { + check_domain(domain)?; } } + Ok(()) } -impl ValueParserFactory for HostAddress { - type Parser = FromStrParser; - fn value_parser() -> Self::Parser { - Self::Parser::new() - } +pub fn address_api( +) -> ParentHandler { + ParentHandler::::new() + .subcommand( + "domain", + ParentHandler::::new() + .subcommand( + "add", + from_fn_async(add_domain::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(|_, a| a) + .no_display() + .with_about("Add an address to this host") + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove_domain::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(|_, a| a) + .no_display() + .with_about("Remove an address from this host") + .with_call_remote::(), + ) + .with_inherited(Kind::inheritance), + ) + .subcommand( + "onion", + ParentHandler::::new() + .subcommand( + "add", + from_fn_async(add_onion::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(|_, a| a) + .no_display() + .with_about("Add an address to this host") + .with_call_remote::(), + ) + .subcommand( + "remove", + from_fn_async(remove_onion::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(|_, a| a) + .no_display() + .with_about("Remove an address from this host") + .with_call_remote::(), + ) + .with_inherited(Kind::inheritance), + ) + .subcommand( + "list", + from_fn_async(list_addresses::) + .with_inherited(Kind::inheritance) + .with_display_serializable() + .with_custom_display_fn(|HandlerArgs { params, .. }, res| { + use prettytable::*; + + if let Some(format) = params.format { + display_serializable(format, res); + return Ok(()); + } + + let mut table = Table::new(); + table.add_row(row![bc => "ADDRESS", "PUBLIC", "ACME PROVIDER"]); + for address in &res { + match address { + HostAddress::Onion { address } => { + table.add_row(row![address, true, "N/A"]); + } + HostAddress::Domain { + address, + public, + acme, + } => { + table.add_row(row![ + address, + *public, + acme.as_ref().map(|a| a.0.as_str()).unwrap_or("NONE") + ]); + } + } + } + + table.print_tty(false)?; + + Ok(()) + }) + .with_about("List addresses for this host") + .with_call_remote::(), + ) +} + +#[derive(Deserialize, Serialize, Parser)] +pub struct AddDomainParams { + pub domain: InternedString, + #[arg(long)] + pub private: bool, + #[arg(long)] + pub acme: Option, +} + +pub async fn add_domain( + ctx: RpcContext, + AddDomainParams { + domain, + private, + acme, + }: AddDomainParams, + inheritance: Kind::Inheritance, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + if let Some(acme) = &acme { + if !db.as_public().as_server_info().as_acme().contains_key(&acme)? { + return Err(Error::new(eyre!("unknown acme provider {}, please run acme.init for this provider first", acme.0), ErrorKind::InvalidRequest)); + } + } + + Kind::host_for(&inheritance, db)? + .as_domains_mut() + .insert( + &domain, + &DomainConfig { + public: !private, + acme, + }, + )?; + check_duplicates(db) + }) + .await?; + Kind::sync_host(&ctx, inheritance).await?; + + Ok(()) +} + +#[derive(Deserialize, Serialize, Parser)] +pub struct RemoveDomainParams { + pub domain: InternedString, +} + +pub async fn remove_domain( + ctx: RpcContext, + RemoveDomainParams { domain }: RemoveDomainParams, + inheritance: Kind::Inheritance, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + Kind::host_for(&inheritance, db)? + .as_domains_mut() + .remove(&domain) + }) + .await?; + Kind::sync_host(&ctx, inheritance).await?; + + Ok(()) +} + +#[derive(Deserialize, Serialize, Parser)] +pub struct OnionParams { + pub onion: String, +} + +pub async fn add_onion( + ctx: RpcContext, + OnionParams { onion }: OnionParams, + inheritance: Kind::Inheritance, +) -> Result<(), Error> { + let onion = onion + .strip_suffix(".onion") + .ok_or_else(|| { + Error::new( + eyre!("onion hostname must end in .onion"), + ErrorKind::InvalidOnionAddress, + ) + })? + .parse::()?; + ctx.db + .mutate(|db| { + db.as_private().as_key_store().as_onion().get_key(&onion)?; + + Kind::host_for(&inheritance, db)? + .as_onions_mut() + .mutate(|a| Ok(a.insert(onion)))?; + check_duplicates(db) + }) + .await?; + + Kind::sync_host(&ctx, inheritance).await?; + + Ok(()) +} + +pub async fn remove_onion( + ctx: RpcContext, + OnionParams { onion }: OnionParams, + inheritance: Kind::Inheritance, +) -> Result<(), Error> { + let onion = onion + .strip_suffix(".onion") + .ok_or_else(|| { + Error::new( + eyre!("onion hostname must end in .onion"), + ErrorKind::InvalidOnionAddress, + ) + })? + .parse::()?; + ctx.db + .mutate(|db| { + Kind::host_for(&inheritance, db)? + .as_onions_mut() + .mutate(|a| Ok(a.remove(&onion))) + }) + .await?; + + Kind::sync_host(&ctx, inheritance).await?; + + Ok(()) +} + +pub async fn list_addresses( + ctx: RpcContext, + _: Empty, + inheritance: Kind::Inheritance, +) -> Result, Error> { + Ok(Kind::host_for(&inheritance, &mut ctx.db.peek().await)? + .de()? + .addresses() + .collect()) } diff --git a/core/startos/src/net/host/binding.rs b/core/startos/src/net/host/binding.rs index 174f0330f..5b726a82d 100644 --- a/core/startos/src/net/host/binding.rs +++ b/core/startos/src/net/host/binding.rs @@ -1,13 +1,19 @@ +use std::collections::BTreeMap; use std::str::FromStr; use clap::builder::ValueParserFactory; +use clap::Parser; use models::{FromStrParser, HostId}; +use rpc_toolkit::{from_fn_async, Context, Empty, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use ts_rs::TS; +use crate::context::{CliContext, RpcContext}; use crate::net::forward::AvailablePorts; +use crate::net::host::HostApiKind; use crate::net::vhost::AlpnInfo; use crate::prelude::*; +use crate::util::serde::{display_serializable, HandlerExtSerde}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)] #[ts(export)] @@ -41,12 +47,14 @@ impl FromStr for BindId { pub struct BindInfo { pub enabled: bool, pub options: BindOptions, - pub lan: LanInfo, + pub net: NetInfo, } + #[derive(Clone, Copy, Debug, Deserialize, Serialize, TS, PartialEq, Eq, PartialOrd, Ord)] #[serde(rename_all = "camelCase")] #[ts(export)] -pub struct LanInfo { +pub struct NetInfo { + pub public: bool, pub assigned_port: Option, pub assigned_ssl_port: Option, } @@ -63,7 +71,8 @@ impl BindInfo { Ok(Self { enabled: true, options, - lan: LanInfo { + net: NetInfo { + public: false, assigned_port, assigned_ssl_port, }, @@ -74,7 +83,7 @@ impl BindInfo { available_ports: &mut AvailablePorts, options: BindOptions, ) -> Result { - let Self { mut lan, .. } = self; + let Self { net: mut lan, .. } = self; if options .secure .map_or(false, |s| !(s.ssl && options.add_ssl.is_some())) @@ -104,7 +113,7 @@ impl BindInfo { Ok(Self { enabled: true, options, - lan, + net: lan, }) } pub fn disable(&mut self) { @@ -137,3 +146,99 @@ pub struct AddSslOptions { // pub add_x_forwarded_headers: bool, // TODO pub alpn: Option, } + +pub fn binding( +) -> ParentHandler { + ParentHandler::::new() + .subcommand( + "list", + from_fn_async(list_bindings::) + .with_inherited(Kind::inheritance) + .with_display_serializable() + .with_custom_display_fn(|HandlerArgs { params, .. }, res| { + use prettytable::*; + + if let Some(format) = params.format { + return Ok(display_serializable(format, res)); + } + + let mut table = Table::new(); + table.add_row(row![bc => "INTERNAL PORT", "ENABLED", "PUBLIC", "EXTERNAL PORT", "EXTERNAL SSL PORT"]); + for (internal, info) in res { + table.add_row(row![ + internal, + info.enabled, + info.net.public, + if let Some(port) = info.net.assigned_port { + port.to_string() + } else { + "N/A".to_owned() + }, + if let Some(port) = info.net.assigned_ssl_port { + port.to_string() + } else { + "N/A".to_owned() + }, + ]); + } + + table.print_tty(false).unwrap(); + + Ok(()) + }) + .with_about("List bindinges for this host") + .with_call_remote::(), + ) + .subcommand( + "set-public", + from_fn_async(set_public::) + .with_metadata("sync_db", Value::Bool(true)) + .with_inherited(Kind::inheritance) + .no_display() + .with_about("Add an binding to this host") + .with_call_remote::(), + ) +} + +pub async fn list_bindings( + ctx: RpcContext, + _: Empty, + inheritance: Kind::Inheritance, +) -> Result, Error> { + Kind::host_for(&inheritance, &mut ctx.db.peek().await)? + .as_bindings() + .de() +} + +#[derive(Deserialize, Serialize, Parser, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export)] +pub struct BindingSetPublicParams { + internal_port: u16, + #[arg(long)] + public: Option, +} + +pub async fn set_public( + ctx: RpcContext, + BindingSetPublicParams { + internal_port, + public, + }: BindingSetPublicParams, + inheritance: Kind::Inheritance, +) -> Result<(), Error> { + ctx.db + .mutate(|db| { + Kind::host_for(&inheritance, db)? + .as_bindings_mut() + .mutate(|b| { + b.get_mut(&internal_port) + .or_not_found(internal_port)? + .net + .public = public.unwrap_or(true); + Ok(()) + }) + }) + .await?; + Kind::sync_host(&ctx, inheritance).await +} diff --git a/core/startos/src/net/host/mod.rs b/core/startos/src/net/host/mod.rs index be5db0f2d..df209cef5 100644 --- a/core/startos/src/net/host/mod.rs +++ b/core/startos/src/net/host/mod.rs @@ -1,31 +1,37 @@ use std::collections::{BTreeMap, BTreeSet}; +use std::future::Future; +use std::panic::RefUnwindSafe; use clap::Parser; use imbl_value::InternedString; +use itertools::Itertools; use models::{HostId, PackageId}; -use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; +use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, OrEmpty, ParentHandler}; use serde::{Deserialize, Serialize}; +use torut::onion::OnionAddressV3; use ts_rs::TS; -use crate::context::{CliContext, RpcContext}; +use crate::context::RpcContext; use crate::db::model::DatabaseModel; use crate::net::forward::AvailablePorts; -use crate::net::host::address::HostAddress; -use crate::net::host::binding::{BindInfo, BindOptions}; +use crate::net::host::address::{address_api, DomainConfig, HostAddress}; +use crate::net::host::binding::{binding, BindInfo, BindOptions}; use crate::net::service_interface::HostnameInfo; use crate::prelude::*; pub mod address; pub mod binding; -#[derive(Debug, Deserialize, Serialize, HasModel, TS)] +#[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] #[serde(rename_all = "camelCase")] #[model = "Model"] #[ts(export)] pub struct Host { - pub kind: HostKind, pub bindings: BTreeMap, - pub addresses: BTreeSet, + #[ts(type = "string[]")] + pub onions: BTreeSet, + #[ts(as = "BTreeMap::")] + pub domains: BTreeMap, /// COMPUTED: NetService::update pub hostname_info: BTreeMap>, // internal port -> Hostnames } @@ -35,29 +41,28 @@ impl AsRef for Host { } } impl Host { - pub fn new(kind: HostKind) -> Self { - Self { - kind, - bindings: BTreeMap::new(), - addresses: BTreeSet::new(), - hostname_info: BTreeMap::new(), - } + pub fn new() -> Self { + Self::default() } - pub fn addresses(&self) -> impl Iterator { - // TODO: handle primary - self.addresses.iter() + pub fn addresses<'a>(&'a self) -> impl Iterator + 'a { + self.onions + .iter() + .cloned() + .map(|address| HostAddress::Onion { address }) + .chain( + self.domains + .iter() + .map( + |(address, DomainConfig { public, acme })| HostAddress::Domain { + address: address.clone(), + public: *public, + acme: acme.clone(), + }, + ), + ) } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] -#[serde(rename_all = "camelCase")] -#[ts(export)] -pub enum HostKind { - Multi, - // Single, - // Static, -} - #[derive(Debug, Default, Deserialize, Serialize, HasModel, TS)] #[model = "Model"] #[ts(export)] @@ -76,10 +81,12 @@ impl Map for Hosts { pub fn host_for<'a>( db: &'a mut DatabaseModel, - package_id: &PackageId, + package_id: Option<&PackageId>, host_id: &HostId, - host_kind: HostKind, ) -> Result<&'a mut Model, Error> { + let Some(package_id) = package_id else { + return Ok(db.as_public_mut().as_server_info_mut().as_host_mut()); + }; fn host_info<'a>( db: &'a mut DatabaseModel, package_id: &PackageId, @@ -103,23 +110,31 @@ pub fn host_for<'a>( None }; host_info(db, package_id)?.upsert(host_id, || { - let mut h = Host::new(host_kind); - h.addresses.insert(HostAddress::Onion { - address: tor_key + let mut h = Host::new(); + h.onions.insert( + tor_key .or_not_found("generated tor key")? .public() .get_onion_address(), - }); + ); Ok(h) }) } +pub fn all_hosts(db: &DatabaseModel) -> impl Iterator, Error>> { + [Ok(db.as_public().as_server_info().as_host())] + .into_iter() + .chain( + [db.as_public().as_package_data().as_entries()] + .into_iter() + .flatten_ok() + .map(|entry| entry.and_then(|(_, v)| v.as_hosts().as_entries())) + .flatten_ok() + .map_ok(|(_, v)| v), + ) +} + impl Model { - pub fn set_kind(&mut self, kind: HostKind) -> Result<(), Error> { - match (self.as_kind().de()?, kind) { - (HostKind::Multi, HostKind::Multi) => Ok(()), - } - } pub fn add_binding( &mut self, available_ports: &mut AvailablePorts, @@ -139,16 +154,78 @@ impl Model { } #[derive(Deserialize, Serialize, Parser)] -pub struct HostParams { +pub struct RequiresPackageId { package: PackageId, } -pub fn host() -> ParentHandler { - ParentHandler::::new() +#[derive(Deserialize, Serialize, Parser)] +pub struct RequiresHostId { + host: HostId, +} + +pub trait HostApiKind: 'static { + type Params: Send + Sync + 'static; + type InheritedParams: Send + Sync + 'static; + type Inheritance: RefUnwindSafe + OrEmpty + Send + Sync + 'static; + fn inheritance(params: Self::Params, inherited: Self::InheritedParams) -> Self::Inheritance; + fn host_for<'a>( + inheritance: &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error>; + fn sync_host( + ctx: &RpcContext, + inheritance: Self::Inheritance, + ) -> impl Future> + Send; +} +pub struct ForPackage; +impl HostApiKind for ForPackage { + type Params = RequiresHostId; + type InheritedParams = PackageId; + type Inheritance = (PackageId, HostId); + fn inheritance( + RequiresHostId { host }: Self::Params, + package: Self::InheritedParams, + ) -> Self::Inheritance { + (package, host) + } + fn host_for<'a>( + (package, host): &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error> { + host_for(db, Some(package), host) + } + async fn sync_host(ctx: &RpcContext, (package, host): Self::Inheritance) -> Result<(), Error> { + let service = ctx.services.get(&package).await; + let service_ref = service.as_ref().or_not_found(&package)?; + service_ref.sync_host(host).await?; + Ok(()) + } +} +pub struct ForServer; +impl HostApiKind for ForServer { + type Params = Empty; + type InheritedParams = Empty; + type Inheritance = Empty; + fn inheritance(_: Self::Params, _: Self::InheritedParams) -> Self::Inheritance { + Empty {} + } + fn host_for<'a>( + _: &Self::Inheritance, + db: &'a mut DatabaseModel, + ) -> Result<&'a mut Model, Error> { + host_for(db, None, &HostId::default()) + } + async fn sync_host(ctx: &RpcContext, _: Self::Inheritance) -> Result<(), Error> { + ctx.os_net_service.sync_host(HostId::default()).await + } +} + +pub fn host_api() -> ParentHandler { + ParentHandler::::new() .subcommand( "list", from_fn_async(list_hosts) - .with_inherited(|HostParams { package }, _| package) + .with_inherited(|RequiresPackageId { package }, _| package) .with_custom_display_fn(|_, ids| { for id in ids { println!("{id}") @@ -159,8 +236,19 @@ pub fn host() -> ParentHandler { ) .subcommand( "address", - address::().with_inherited(|HostParams { package }, _| package), + address_api::() + .with_inherited(|RequiresPackageId { package }, _| package), ) + .subcommand( + "binding", + binding::().with_inherited(|RequiresPackageId { package }, _| package), + ) +} + +pub fn server_host_api() -> ParentHandler { + ParentHandler::::new() + .subcommand("address", address_api::()) + .subcommand("binding", binding::()) } pub async fn list_hosts( @@ -178,122 +266,3 @@ pub async fn list_hosts( .into_hosts() .keys() } - -#[derive(Deserialize, Serialize, Parser)] -pub struct AddressApiParams { - host: HostId, -} - -pub fn address() -> ParentHandler { - ParentHandler::::new() - .subcommand( - "add", - from_fn_async(add_address) - .with_inherited(|AddressApiParams { host }, package| (package, host)) - .no_display() - .with_about("Add an address to this host") - .with_call_remote::(), - ) - .subcommand( - "remove", - from_fn_async(remove_address) - .with_inherited(|AddressApiParams { host }, package| (package, host)) - .no_display() - .with_about("Remove an address from this host") - .with_call_remote::(), - ) - .subcommand( - "list", - from_fn_async(list_addresses) - .with_inherited(|AddressApiParams { host }, package| (package, host)) - .with_custom_display_fn(|_, res| { - for address in res { - println!("{address}") - } - Ok(()) - }) - .with_about("List addresses for this host") - .with_call_remote::(), - ) -} - -#[derive(Deserialize, Serialize, Parser)] -pub struct AddressParams { - pub address: HostAddress, -} - -pub async fn add_address( - ctx: RpcContext, - AddressParams { address }: AddressParams, - (package, host): (PackageId, HostId), -) -> Result<(), Error> { - ctx.db - .mutate(|db| { - if let HostAddress::Onion { address } = address { - db.as_private() - .as_key_store() - .as_onion() - .get_key(&address)?; - } - - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? - .as_addresses_mut() - .mutate(|a| Ok(a.insert(address))) - }) - .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; - - Ok(()) -} - -pub async fn remove_address( - ctx: RpcContext, - AddressParams { address }: AddressParams, - (package, host): (PackageId, HostId), -) -> Result<(), Error> { - ctx.db - .mutate(|db| { - db.as_public_mut() - .as_package_data_mut() - .as_idx_mut(&package) - .or_not_found(&package)? - .as_hosts_mut() - .as_idx_mut(&host) - .or_not_found(&host)? - .as_addresses_mut() - .mutate(|a| Ok(a.remove(&address))) - }) - .await?; - let service = ctx.services.get(&package).await; - let service_ref = service.as_ref().or_not_found(&package)?; - service_ref.update_host(host).await?; - - Ok(()) -} - -pub async fn list_addresses( - ctx: RpcContext, - _: Empty, - (package, host): (PackageId, HostId), -) -> Result, Error> { - ctx.db - .peek() - .await - .into_public() - .into_package_data() - .into_idx(&package) - .or_not_found(&package)? - .into_hosts() - .into_idx(&host) - .or_not_found(&host)? - .into_addresses() - .de() -} diff --git a/core/startos/src/net/keys.rs b/core/startos/src/net/keys.rs index 866b2ca06..2cfcb025d 100644 --- a/core/startos/src/net/keys.rs +++ b/core/startos/src/net/keys.rs @@ -21,7 +21,9 @@ impl KeyStore { local_certs: CertStore::new(account)?, acme: AcmeCertStore::new(), }; - res.onion.insert(account.tor_key.clone()); + for tor_key in account.tor_keys.iter().cloned() { + res.onion.insert(tor_key); + } Ok(res) } } diff --git a/core/startos/src/net/mod.rs b/core/startos/src/net/mod.rs index 53b94454d..49d3560ef 100644 --- a/core/startos/src/net/mod.rs +++ b/core/startos/src/net/mod.rs @@ -1,13 +1,13 @@ use rpc_toolkit::{Context, HandlerExt, ParentHandler}; pub mod acme; -pub mod dhcp; pub mod dns; pub mod forward; pub mod host; pub mod keys; pub mod mdns; pub mod net_controller; +pub mod network_interface; pub mod service_interface; pub mod ssl; pub mod static_server; @@ -17,20 +17,23 @@ pub mod vhost; pub mod web_server; pub mod wifi; -pub const PACKAGE_CERT_PATH: &str = "/var/lib/embassy/ssl"; - pub fn net() -> ParentHandler { ParentHandler::new() .subcommand( "tor", tor::tor::().with_about("Tor commands such as list-services, logs, and reset"), ) - .subcommand( - "dhcp", - dhcp::dhcp::().with_about("Command to update IP assigned from dhcp"), - ) .subcommand( "acme", acme::acme::().with_about("Setup automatic clearnet certificate acquisition"), ) + .subcommand( + "network-interface", + network_interface::network_interface_api::() + .with_about("View and edit network interface configurations"), + ) + .subcommand( + "vhost", + vhost::vhost_api::().with_about("Manage ssl virtual host proxy"), + ) } diff --git a/core/startos/src/net/net_controller.rs b/core/startos/src/net/net_controller.rs index a8beaf55f..c3d0e8676 100644 --- a/core/startos/src/net/net_controller.rs +++ b/core/startos/src/net/net_controller.rs @@ -5,7 +5,10 @@ use std::sync::{Arc, Weak}; use color_eyre::eyre::eyre; use imbl::OrdMap; use imbl_value::InternedString; +use ipnet::IpNet; use models::{HostId, OptionExt, PackageId}; +use tokio::sync::Mutex; +use tokio::task::JoinHandle; use torut::onion::{OnionAddressV3, TorSecretKeyV3}; use tracing::instrument; @@ -15,135 +18,55 @@ use crate::hostname::Hostname; use crate::net::dns::DnsController; use crate::net::forward::LanPortForwardController; use crate::net::host::address::HostAddress; -use crate::net::host::binding::{AddSslOptions, BindId, BindOptions, LanInfo}; -use crate::net::host::{host_for, Host, HostKind, Hosts}; +use crate::net::host::binding::{AddSslOptions, BindId, BindOptions}; +use crate::net::host::{host_for, Host, Hosts}; +use crate::net::network_interface::NetworkInterfaceController; use crate::net::service_interface::{HostnameInfo, IpHostname, OnionHostname}; use crate::net::tor::TorController; -use crate::net::vhost::{AlpnInfo, VHostController}; +use crate::net::utils::ipv6_is_local; +use crate::net::vhost::{AlpnInfo, TargetInfo, VHostController}; use crate::prelude::*; use crate::util::serde::MaybeUtf8String; use crate::HOST_IP; -pub struct PreInitNetController { - pub db: TypedPatchDb, - tor: TorController, - vhost: VHostController, - os_bindings: Vec>, - server_hostnames: Vec>, -} -impl PreInitNetController { - #[instrument(skip_all)] - pub async fn init( - db: TypedPatchDb, - tor_control: SocketAddr, - tor_socks: SocketAddr, - hostname: &Hostname, - os_tor_key: TorSecretKeyV3, - ) -> Result { - let mut res = Self { - db: db.clone(), - tor: TorController::new(tor_control, tor_socks), - vhost: VHostController::new(db), - os_bindings: Vec::new(), - server_hostnames: Vec::new(), - }; - res.add_os_bindings(hostname, os_tor_key).await?; - Ok(res) - } - - async fn add_os_bindings( - &mut self, - hostname: &Hostname, - tor_key: TorSecretKeyV3, - ) -> Result<(), Error> { - let alpn = Err(AlpnInfo::Specified(vec![ - MaybeUtf8String("http/1.1".into()), - MaybeUtf8String("h2".into()), - ])); - - self.server_hostnames = vec![ - // LAN IP - None, - // Internal DNS - Some("embassy".into()), - Some("startos".into()), - // localhost - Some("localhost".into()), - Some(hostname.no_dot_host_name()), - // LAN mDNS - Some(hostname.local_domain_name()), - ]; - - for hostname in self.server_hostnames.iter().cloned() { - self.os_bindings.push( - self.vhost - .add(hostname, 443, ([127, 0, 0, 1], 80).into(), alpn.clone()) - .await?, - ); - } - - // Tor - self.os_bindings.push( - self.vhost - .add( - Some(InternedString::from_display( - &tor_key.public().get_onion_address(), - )), - 443, - ([127, 0, 0, 1], 80).into(), - alpn.clone(), - ) - .await?, - ); - self.os_bindings.extend( - self.tor - .add( - tor_key, - vec![ - (80, ([127, 0, 0, 1], 80).into()), // http - (443, ([127, 0, 0, 1], 443).into()), // https - ], - ) - .await?, - ); - - Ok(()) - } -} - pub struct NetController { - db: TypedPatchDb, + pub(crate) db: TypedPatchDb, pub(super) tor: TorController, pub(super) vhost: VHostController, + pub(crate) net_iface: Arc, pub(super) dns: DnsController, pub(super) forward: LanPortForwardController, - pub(super) os_bindings: Vec>, pub(super) server_hostnames: Vec>, } impl NetController { pub async fn init( - PreInitNetController { - db, - tor, - vhost, - os_bindings, - server_hostnames, - }: PreInitNetController, - dns_bind: &[SocketAddr], + db: TypedPatchDb, + tor_control: SocketAddr, + tor_socks: SocketAddr, + hostname: &Hostname, ) -> Result { - let mut res = Self { - db, - tor, - vhost, - dns: DnsController::init(dns_bind).await?, - forward: LanPortForwardController::new(), - os_bindings, - server_hostnames, - }; - res.os_bindings - .push(res.dns.add(None, HOST_IP.into()).await?); - Ok(res) + let net_iface = Arc::new(NetworkInterfaceController::new(db.clone())); + Ok(Self { + db: db.clone(), + tor: TorController::new(tor_control, tor_socks), + vhost: VHostController::new(db, net_iface.clone()), + dns: DnsController::init(net_iface.lxcbr_status()).await?, + forward: LanPortForwardController::new(net_iface.subscribe()), + net_iface, + server_hostnames: vec![ + // LAN IP + None, + // Internal DNS + Some("embassy".into()), + Some("startos".into()), + // localhost + Some("localhost".into()), + Some(hostname.no_dot_host_name()), + // LAN mDNS + Some(hostname.local_domain_name()), + ], + }) } #[instrument(skip_all)] @@ -154,42 +77,65 @@ impl NetController { ) -> Result { let dns = self.dns.add(Some(package.clone()), ip).await?; - let mut res = NetService { - shutdown: false, - id: package, + let res = NetService::new(NetServiceData { + id: Some(package), ip, dns, controller: Arc::downgrade(self), binds: BTreeMap::new(), - }; + })?; res.clear_bindings(Default::default()).await?; Ok(res) } + + pub async fn os_bindings(self: &Arc) -> Result { + let dns = self.dns.add(None, HOST_IP.into()).await?; + + let service = NetService::new(NetServiceData { + id: None, + ip: [127, 0, 0, 1].into(), + dns, + controller: Arc::downgrade(self), + binds: BTreeMap::new(), + })?; + service.clear_bindings(Default::default()).await?; + service + .bind( + HostId::default(), + 80, + BindOptions { + preferred_external_port: 80, + add_ssl: Some(AddSslOptions { + preferred_external_port: 443, + alpn: Some(AlpnInfo::Specified(vec![ + MaybeUtf8String("http/1.1".into()), + MaybeUtf8String("h2".into()), + ])), + }), + secure: None, + }, + ) + .await?; + + Ok(service) + } } #[derive(Default, Debug)] struct HostBinds { - lan: BTreeMap< - u16, - ( - LanInfo, - Option, - BTreeSet, - Vec>, - ), - >, + forwards: BTreeMap)>, + vhosts: BTreeMap<(Option, u16), (TargetInfo, Arc<()>)>, tor: BTreeMap, Vec>)>, } -pub struct NetService { - shutdown: bool, - id: PackageId, +pub struct NetServiceData { + id: Option, ip: Ipv4Addr, dns: Arc<()>, controller: Weak, binds: BTreeMap, } -impl NetService { +impl NetServiceData { fn net_controller(&self) -> Result, Error> { Weak::upgrade(&self.controller).ok_or_else(|| { Error::new( @@ -199,49 +145,54 @@ impl NetService { }) } - pub async fn bind( + async fn clear_bindings( &mut self, - kind: HostKind, - id: HostId, - internal_port: u16, - options: BindOptions, + ctrl: &NetController, + except: BTreeSet, ) -> Result<(), Error> { - dbg!("bind", &kind, &id, internal_port, &options); - let pkg_id = &self.id; - let host = self - .net_controller()? - .db - .mutate(|db| { - let mut ports = db.as_private().as_available_ports().de()?; - let host = host_for(db, pkg_id, &id, kind)?; - host.add_binding(&mut ports, internal_port, options)?; - let host = host.de()?; - db.as_private_mut().as_available_ports_mut().ser(&ports)?; - Ok(host) - }) - .await?; - self.update(id, host).await - } - - pub async fn clear_bindings(&mut self, except: BTreeSet) -> Result<(), Error> { - let pkg_id = &self.id; - let hosts = self - .net_controller()? - .db - .mutate(|db| { - let mut res = Hosts::default(); - for (host_id, host) in db - .as_public_mut() - .as_package_data_mut() - .as_idx_mut(pkg_id) - .or_not_found(pkg_id)? - .as_hosts_mut() - .as_entries_mut()? - { + if let Some(pkg_id) = &self.id { + let hosts = ctrl + .db + .mutate(|db| { + let mut res = Hosts::default(); + for (host_id, host) in db + .as_public_mut() + .as_package_data_mut() + .as_idx_mut(pkg_id) + .or_not_found(pkg_id)? + .as_hosts_mut() + .as_entries_mut()? + { + host.as_bindings_mut().mutate(|b| { + for (internal_port, info) in b { + if !except.contains(&BindId { + id: host_id.clone(), + internal_port: *internal_port, + }) { + info.disable(); + } + } + Ok(()) + })?; + res.0.insert(host_id, host.de()?); + } + Ok(res) + }) + .await?; + let mut errors = ErrorCollection::new(); + for (id, host) in hosts.0 { + errors.handle(self.update(ctrl, id, host).await); + } + errors.into_result() + } else { + let host = ctrl + .db + .mutate(|db| { + let host = db.as_public_mut().as_server_info_mut().as_host_mut(); host.as_bindings_mut().mutate(|b| { for (internal_port, info) in b { if !except.contains(&BindId { - id: host_id.clone(), + id: HostId::default(), internal_port: *internal_port, }) { info.disable(); @@ -249,148 +200,169 @@ impl NetService { } Ok(()) })?; - res.0.insert(host_id, host.de()?); - } - Ok(res) - }) - .await?; - let mut errors = ErrorCollection::new(); - for (id, host) in hosts.0 { - errors.handle(self.update(id, host).await); + host.de() + }) + .await?; + self.update(ctrl, HostId::default(), host).await } - errors.into_result() } - pub async fn update(&mut self, id: HostId, host: Host) -> Result<(), Error> { - let ctrl = self.net_controller()?; - let mut hostname_info = BTreeMap::new(); + async fn update(&mut self, ctrl: &NetController, id: HostId, host: Host) -> Result<(), Error> { + let mut forwards: BTreeMap = BTreeMap::new(); + let mut vhosts: BTreeMap<(Option, u16), TargetInfo> = BTreeMap::new(); + let mut tor: BTreeMap)> = + BTreeMap::new(); + let mut hostname_info: BTreeMap> = BTreeMap::new(); let binds = self.binds.entry(id.clone()).or_default(); let peek = ctrl.db.peek().await; // LAN let server_info = peek.as_public().as_server_info(); - let ip_info = server_info.as_ip_info().de()?; + let net_ifaces = ctrl.net_iface.ip_info(); let hostname = server_info.as_hostname().de()?; for (port, bind) in &host.bindings { if !bind.enabled { continue; } - let old_lan_bind = binds.lan.remove(port); - let lan_bind = old_lan_bind - .as_ref() - .filter(|(external, ssl, _, _)| { - ssl == &bind.options.add_ssl && bind.lan == *external - }) - .cloned(); // only keep existing binding if relevant details match - if bind.lan.assigned_port.is_some() || bind.lan.assigned_ssl_port.is_some() { - let new_lan_bind = if let Some(b) = lan_bind { - b - } else { - let mut rcs = Vec::with_capacity(2 + host.addresses.len()); - let mut hostnames = BTreeSet::new(); - if let Some(ssl) = &bind.options.add_ssl { - let external = bind - .lan - .assigned_ssl_port - .or_not_found("assigned ssl port")?; - let target = (self.ip, *port).into(); - let connect_ssl = if let Some(alpn) = ssl.alpn.clone() { - Err(alpn) + if bind.net.assigned_port.is_some() || bind.net.assigned_ssl_port.is_some() { + let mut hostnames = BTreeSet::new(); + if let Some(ssl) = &bind.options.add_ssl { + let external = bind + .net + .assigned_ssl_port + .or_not_found("assigned ssl port")?; + let addr = (self.ip, *port).into(); + let connect_ssl = if let Some(alpn) = ssl.alpn.clone() { + Err(alpn) + } else { + if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { + Ok(()) } else { - if bind.options.secure.as_ref().map_or(false, |s| s.ssl) { - Ok(()) - } else { - Err(AlpnInfo::Reflect) - } - }; - for hostname in ctrl.server_hostnames.iter().cloned() { - rcs.push( - ctrl.vhost - .add(hostname, external, target, connect_ssl.clone()) - .await?, - ); + Err(AlpnInfo::Reflect) } - for address in host.addresses() { - match address { - HostAddress::Onion { address } => { - let hostname = InternedString::from_display(address); - if hostnames.insert(hostname.clone()) { - rcs.push( - ctrl.vhost - .add( - Some(hostname), - external, - target, - connect_ssl.clone(), - ) - .await?, - ); - } + }; + for hostname in ctrl.server_hostnames.iter().cloned() { + vhosts.insert( + (hostname, external), + TargetInfo { + public: bind.net.public, + acme: None, + addr, + connect_ssl: connect_ssl.clone(), + }, + ); + } + for address in host.addresses() { + match address { + HostAddress::Onion { address } => { + let hostname = InternedString::from_display(&address); + if hostnames.insert(hostname.clone()) { + vhosts.insert( + (Some(hostname), external), + TargetInfo { + public: false, + acme: None, + addr, + connect_ssl: connect_ssl.clone(), + }, + ); } - HostAddress::Domain { address } => { - if hostnames.insert(address.clone()) { - let address = Some(address.clone()); - rcs.push( - ctrl.vhost - .add( - address.clone(), - external, - target, - connect_ssl.clone(), - ) - .await?, - ); - if ssl.preferred_external_port == 443 { - rcs.push( - ctrl.vhost - .add( - address.clone(), - 5443, - target, - connect_ssl.clone(), - ) - .await?, + } + HostAddress::Domain { + address, + public, + acme, + } => { + if hostnames.insert(address.clone()) { + let address = Some(address.clone()); + if ssl.preferred_external_port == 443 { + if public && bind.net.public { + vhosts.insert( + (address.clone(), 5443), + TargetInfo { + public: false, + acme: acme.clone(), + addr, + connect_ssl: connect_ssl.clone(), + }, ); } + vhosts.insert( + (address.clone(), 443), + TargetInfo { + public: public && bind.net.public, + acme, + addr, + connect_ssl: connect_ssl.clone(), + }, + ); + } else { + vhosts.insert( + (address.clone(), external), + TargetInfo { + public: public && bind.net.public, + acme, + addr, + connect_ssl: connect_ssl.clone(), + }, + ); } } } } } - if let Some(security) = bind.options.secure { - if bind.options.add_ssl.is_some() && security.ssl { - // doesn't make sense to have 2 listening ports, both with ssl - } else { - let external = - bind.lan.assigned_port.or_not_found("assigned lan port")?; - rcs.push(ctrl.forward.add(external, (self.ip, *port).into()).await?); - } + } + if let Some(security) = bind.options.secure { + if bind.options.add_ssl.is_some() && security.ssl { + // doesn't make sense to have 2 listening ports, both with ssl + } else { + let external = bind.net.assigned_port.or_not_found("assigned lan port")?; + forwards.insert(external, ((self.ip, *port).into(), bind.net.public)); } - (bind.lan, bind.options.add_ssl.clone(), hostnames, rcs) - }; + } let mut bind_hostname_info: Vec = hostname_info.remove(port).unwrap_or_default(); - for (interface, ip_info) in &ip_info { - bind_hostname_info.push(HostnameInfo::Ip { - network_interface_id: interface.clone(), - public: false, - hostname: IpHostname::Local { - value: InternedString::from_display(&{ - let hostname = &hostname; - lazy_format!("{hostname}.local") - }), - port: new_lan_bind.0.assigned_port, - ssl_port: new_lan_bind.0.assigned_ssl_port, - }, - }); + for (interface, public, ip_info) in + net_ifaces.iter().filter_map(|(interface, info)| { + if let Some(ip_info) = &info.ip_info { + Some((interface, info.public(), ip_info)) + } else { + None + } + }) + { + if !public { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public: false, + hostname: IpHostname::Local { + value: InternedString::from_display(&{ + let hostname = &hostname; + lazy_format!("{hostname}.local") + }), + port: bind.net.assigned_port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); + } for address in host.addresses() { - if let HostAddress::Domain { address } = address { - if let Some(ssl) = &new_lan_bind.1 { - if ssl.preferred_external_port == 443 { + if let HostAddress::Domain { + address, + public: domain_public, + .. + } = address + { + if !public || (domain_public && bind.net.public) { + if bind + .options + .add_ssl + .as_ref() + .map_or(false, |ssl| ssl.preferred_external_port == 443) + { bind_hostname_info.push(HostnameInfo::Ip { network_interface_id: interface.clone(), - public: false, + public: public && domain_public && bind.net.public, // TODO: check if port forward is active hostname: IpHostname::Domain { domain: address.clone(), subdomain: None, @@ -398,71 +370,65 @@ impl NetService { ssl_port: Some(443), }, }); + } else { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public, + hostname: IpHostname::Domain { + domain: address.clone(), + subdomain: None, + port: bind.net.assigned_port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); } } } } - if let Some(ipv4) = ip_info.ipv4 { - bind_hostname_info.push(HostnameInfo::Ip { - network_interface_id: interface.clone(), - public: false, - hostname: IpHostname::Ipv4 { - value: ipv4, - port: new_lan_bind.0.assigned_port, - ssl_port: new_lan_bind.0.assigned_ssl_port, - }, - }); - } - if let Some(ipv6) = ip_info.ipv6 { - bind_hostname_info.push(HostnameInfo::Ip { - network_interface_id: interface.clone(), - public: false, - hostname: IpHostname::Ipv6 { - value: ipv6, - port: new_lan_bind.0.assigned_port, - ssl_port: new_lan_bind.0.assigned_ssl_port, - }, - }); + if !public || bind.net.public { + if let Some(wan_ip) = ip_info.wan_ip.filter(|_| public) { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public, + hostname: IpHostname::Ipv4 { + value: wan_ip, + port: bind.net.assigned_port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); + } + for ipnet in &ip_info.subnets { + match ipnet { + IpNet::V4(net) => { + if !public { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public, + hostname: IpHostname::Ipv4 { + value: net.addr(), + port: bind.net.assigned_port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); + } + } + IpNet::V6(net) => { + bind_hostname_info.push(HostnameInfo::Ip { + network_interface_id: interface.clone(), + public: public && !ipv6_is_local(net.addr()), + hostname: IpHostname::Ipv6 { + value: net.addr(), + scope_id: ip_info.scope_id, + port: bind.net.assigned_port, + ssl_port: bind.net.assigned_ssl_port, + }, + }); + } + } + } } } hostname_info.insert(*port, bind_hostname_info); - binds.lan.insert(*port, new_lan_bind); - } - if let Some((lan, _, hostnames, _)) = old_lan_bind { - if let Some(external) = lan.assigned_ssl_port { - for hostname in ctrl.server_hostnames.iter().cloned() { - ctrl.vhost.gc(hostname, external).await?; - } - for hostname in hostnames { - ctrl.vhost.gc(Some(hostname), external).await?; - } - } - if let Some(external) = lan.assigned_port { - ctrl.forward.gc(external).await?; - } - } - } - let mut removed = BTreeSet::new(); - binds.lan.retain(|internal, (external, _, hostnames, _)| { - if host.bindings.get(internal).map_or(false, |b| b.enabled) { - true - } else { - removed.insert((*external, std::mem::take(hostnames))); - - false - } - }); - for (lan, hostnames) in removed { - if let Some(external) = lan.assigned_ssl_port { - for hostname in ctrl.server_hostnames.iter().cloned() { - ctrl.vhost.gc(hostname, external).await?; - } - for hostname in hostnames { - ctrl.vhost.gc(Some(hostname), external).await?; - } - } - if let Some(external) = lan.assigned_port { - ctrl.forward.gc(external).await?; } } @@ -481,7 +447,7 @@ impl NetService { SocketAddr::from((self.ip, *internal)), ); if let (Some(ssl), Some(ssl_internal)) = - (&info.options.add_ssl, info.lan.assigned_ssl_port) + (&info.options.add_ssl, info.net.assigned_ssl_port) { tor_binds.insert( ssl.preferred_external_port, @@ -506,31 +472,13 @@ impl NetService { } } - let mut keep_tor_addrs = BTreeSet::new(); - for tor_addr in host.addresses().filter_map(|a| { - if let HostAddress::Onion { address } = a { - Some(address) - } else { - None - } - }) { - keep_tor_addrs.insert(tor_addr); - let old_tor_bind = binds.tor.remove(tor_addr); - let tor_bind = old_tor_bind.filter(|(ports, _)| ports == &tor_binds); - let new_tor_bind = if let Some(tor_bind) = tor_bind { - tor_bind - } else { - let key = peek - .as_private() - .as_key_store() - .as_onion() - .get_key(tor_addr)?; - let rcs = ctrl - .tor - .add(key, tor_binds.clone().into_iter().collect()) - .await?; - (tor_binds.clone(), rcs) - }; + for tor_addr in host.onions.iter() { + let key = peek + .as_private() + .as_key_store() + .as_onion() + .get_key(tor_addr)?; + tor.insert(key.public().get_onion_address(), (key, tor_binds.clone())); for (internal, ports) in &tor_hostname_ports { let mut bind_hostname_info = hostname_info.remove(internal).unwrap_or_default(); bind_hostname_info.push(HostnameInfo::Onion { @@ -542,18 +490,93 @@ impl NetService { }); hostname_info.insert(*internal, bind_hostname_info); } - binds.tor.insert(tor_addr.clone(), new_tor_bind); } - for addr in binds.tor.keys() { - if !keep_tor_addrs.contains(addr) { - ctrl.tor.gc(Some(addr.clone()), None).await?; + + let all = binds + .forwards + .keys() + .chain(forwards.keys()) + .copied() + .collect::>(); + for external in all { + let mut prev = binds.forwards.remove(&external); + if let Some((internal, public)) = forwards.remove(&external) { + prev = prev.filter(|(i, p, _)| i == &internal && *p == public); + binds.forwards.insert( + external, + if let Some(prev) = prev { + prev + } else { + ( + internal, + public, + ctrl.forward.add(external, public, internal).await?, + ) + }, + ); + } + } + ctrl.forward.gc().await?; + + let all = binds + .vhosts + .keys() + .chain(vhosts.keys()) + .cloned() + .collect::>(); + for key in all { + let mut prev = binds.vhosts.remove(&key); + if let Some(target) = vhosts.remove(&key) { + prev = prev.filter(|(t, _)| t == &target); + binds.vhosts.insert( + key.clone(), + if let Some(prev) = prev { + prev + } else { + (target.clone(), ctrl.vhost.add(key.0, key.1, target)?) + }, + ); + } else { + if let Some((_, rc)) = prev { + drop(rc); + ctrl.vhost.gc(key.0, key.1); + } } } - self.net_controller()? - .db + let all = binds + .tor + .keys() + .chain(tor.keys()) + .cloned() + .collect::>(); + for onion in all { + let mut prev = binds.tor.remove(&onion); + if let Some((key, tor_binds)) = tor.remove(&onion) { + prev = prev.filter(|(b, _)| b == &tor_binds); + binds.tor.insert( + onion, + if let Some(prev) = prev { + prev + } else { + let rcs = ctrl + .tor + .add(key, tor_binds.iter().map(|(k, v)| (*k, *v)).collect()) + .await?; + (tor_binds, rcs) + }, + ); + } else { + if let Some((_, rc)) = prev { + drop(rc); + ctrl.tor.gc(Some(onion), None).await?; + } + } + } + + ctrl.db .mutate(|db| { - host_for(db, &self.id, &id, host.kind)? + host_for(db, self.id.as_ref(), &id)? .as_hostname_info_mut() .ser(&hostname_info) }) @@ -561,13 +584,137 @@ impl NetService { Ok(()) } + async fn update_all(&mut self) -> Result<(), Error> { + let ctrl = self.net_controller()?; + if let Some(id) = self.id.clone() { + for (host_id, host) in ctrl + .db + .peek() + .await + .as_public() + .as_package_data() + .as_idx(&id) + .or_not_found(&id)? + .as_hosts() + .as_entries()? + { + tracing::info!("Updating host {host_id} for {id}"); + self.update(&*ctrl, host_id.clone(), host.de()?).await?; + tracing::info!("Updated host {host_id} for {id}"); + } + } else { + tracing::info!("Updating host for Main UI"); + self.update( + &*ctrl, + HostId::default(), + ctrl.db + .peek() + .await + .as_public() + .as_server_info() + .as_host() + .de()?, + ) + .await?; + tracing::info!("Updated host for Main UI"); + } + Ok(()) + } +} + +pub struct NetService { + shutdown: bool, + data: Arc>, + sync_task: JoinHandle<()>, +} +impl NetService { + fn dummy() -> Self { + Self { + shutdown: true, + data: Arc::new(Mutex::new(NetServiceData { + id: None, + ip: Ipv4Addr::new(0, 0, 0, 0), + dns: Default::default(), + controller: Default::default(), + binds: BTreeMap::new(), + })), + sync_task: tokio::spawn(futures::future::ready(())), + } + } + + fn new(data: NetServiceData) -> Result { + let mut ip_info = data.net_controller()?.net_iface.subscribe(); + let data = Arc::new(Mutex::new(data)); + let thread_data = data.clone(); + let sync_task = tokio::spawn(async move { + loop { + if let Err(e) = thread_data.lock().await.update_all().await { + tracing::error!("Failed to update network info: {e}"); + tracing::debug!("{e:?}"); + } + ip_info.changed().await; + } + }); + Ok(Self { + shutdown: false, + data, + sync_task, + }) + } + + pub async fn bind( + &self, + id: HostId, + internal_port: u16, + options: BindOptions, + ) -> Result<(), Error> { + let mut data = self.data.lock().await; + let pkg_id = &data.id; + let ctrl = data.net_controller()?; + let host = ctrl + .db + .mutate(|db| { + let mut ports = db.as_private().as_available_ports().de()?; + let host = host_for(db, pkg_id.as_ref(), &id)?; + host.add_binding(&mut ports, internal_port, options)?; + let host = host.de()?; + db.as_private_mut().as_available_ports_mut().ser(&ports)?; + Ok(host) + }) + .await?; + data.update(&*ctrl, id, host).await + } + + pub async fn clear_bindings(&self, except: BTreeSet) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + data.clear_bindings(&*ctrl, except).await + } + + pub async fn update(&self, id: HostId, host: Host) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + data.update(&*ctrl, id, host).await + } + + pub async fn sync_host(&self, id: HostId) -> Result<(), Error> { + let mut data = self.data.lock().await; + let ctrl = data.net_controller()?; + let host = host_for(&mut ctrl.db.peek().await, data.id.as_ref(), &id)?.de()?; + data.update(&*ctrl, id, host).await + } + pub async fn remove_all(mut self) -> Result<(), Error> { - self.shutdown = true; - if let Some(ctrl) = Weak::upgrade(&self.controller) { - self.clear_bindings(Default::default()).await?; + self.sync_task.abort(); + let mut data = self.data.lock().await; + if let Some(ctrl) = Weak::upgrade(&data.controller) { + self.shutdown = true; + data.clear_bindings(&*ctrl, Default::default()).await?; + drop(ctrl); Ok(()) } else { + self.shutdown = true; tracing::warn!("NetService dropped after NetController is shutdown"); Err(Error::new( eyre!("NetController is shutdown"), @@ -576,49 +723,15 @@ impl NetService { } } - pub fn get_ip(&self) -> Ipv4Addr { - self.ip - } - - pub fn get_lan_port(&self, host_id: HostId, internal_port: u16) -> Result { - let host_id_binds = self.binds.get_key_value(&host_id); - match host_id_binds { - Some((_, binds)) => { - if let Some((lan, _, _, _)) = binds.lan.get(&internal_port) { - Ok(*lan) - } else { - Err(Error::new( - eyre!( - "Internal Port {} not found in NetService binds", - internal_port - ), - crate::ErrorKind::NotFound, - )) - } - } - None => Err(Error::new( - eyre!("HostID {} not found in NetService binds", host_id), - crate::ErrorKind::NotFound, - )), - } + pub async fn get_ip(&self) -> Ipv4Addr { + self.data.lock().await.ip } } impl Drop for NetService { fn drop(&mut self) { if !self.shutdown { - tracing::debug!("Dropping NetService for {}", self.id); - let svc = std::mem::replace( - self, - NetService { - shutdown: true, - id: Default::default(), - ip: Ipv4Addr::new(0, 0, 0, 0), - dns: Default::default(), - controller: Default::default(), - binds: BTreeMap::new(), - }, - ); + let svc = std::mem::replace(self, Self::dummy()); tokio::spawn(async move { svc.remove_all().await.log_err() }); } } diff --git a/core/startos/src/net/network_interface.rs b/core/startos/src/net/network_interface.rs new file mode 100644 index 000000000..8c99f0de9 --- /dev/null +++ b/core/startos/src/net/network_interface.rs @@ -0,0 +1,1113 @@ +use std::collections::{BTreeMap, BTreeSet}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::sync::{Arc, Weak}; +use std::task::Poll; +use std::time::Duration; + +use clap::Parser; +use futures::{FutureExt, Stream, StreamExt, TryStreamExt}; +use helpers::NonDetachingJoinHandle; +use imbl_value::InternedString; +use ipnet::IpNet; +use itertools::Itertools; +use nix::net::if_::if_nametoindex; +use patch_db::json_ptr::JsonPointer; +use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler}; +use serde::{Deserialize, Serialize}; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::process::Command; +use ts_rs::TS; +use zbus::proxy::{PropertyChanged, PropertyStream, SignalStream}; +use zbus::zvariant::{ + DeserializeDict, Dict, OwnedObjectPath, OwnedValue, Type as ZType, Value as ZValue, +}; +use zbus::{proxy, Connection}; + +use crate::context::{CliContext, RpcContext}; +use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceType}; +use crate::db::model::Database; +use crate::net::forward::START9_BRIDGE_IFACE; +use crate::net::utils::{ipv6_is_link_local, ipv6_is_local}; +use crate::net::web_server::Accept; +use crate::prelude::*; +use crate::util::future::Until; +use crate::util::io::open_file; +use crate::util::serde::{display_serializable, HandlerExtSerde}; +use crate::util::sync::{SyncMutex, Watch}; +use crate::util::Invoke; + +pub fn network_interface_api() -> ParentHandler { + ParentHandler::new() + .subcommand( + "list", + from_fn_async(list_interfaces) + .with_display_serializable() + .with_custom_display_fn(|HandlerArgs { params, .. }, res| { + use prettytable::*; + + if let Some(format) = params.format { + return Ok(display_serializable(format, res)); + } + + let mut table = Table::new(); + table.add_row(row![bc => "INTERFACE", "TYPE", "PUBLIC", "ADDRESSES", "WAN IP"]); + for (iface, info) in res { + table.add_row(row![ + iface, + info.ip_info.as_ref() + .and_then(|ip_info| ip_info.device_type) + .map_or_else(|| "UNKNOWN".to_owned(), |ty| format!("{ty:?}")), + info.public(), + info.ip_info.as_ref().map_or_else( + || "".to_owned(), + |ip_info| ip_info.subnets + .iter() + .map(|ipnet| match ipnet.addr() { + IpAddr::V4(ip) => format!("{ip}/{}", ipnet.prefix_len()), + IpAddr::V6(ip) => format!( + "[{ip}%{}]/{}", + ip_info.scope_id, + ipnet.prefix_len() + ), + }) + .join(", ")), + info.ip_info.as_ref() + .and_then(|ip_info| ip_info.wan_ip) + .map_or_else(|| "N/A".to_owned(), |ip| ip.to_string()) + ]); + } + + table.print_tty(false).unwrap(); + + Ok(()) + }) + .with_about("Show network interfaces StartOS can listen on") + .with_call_remote::(), + ) + .subcommand( + "set-public", + from_fn_async(set_public) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Indicate whether this interface is publicly addressable") + .with_call_remote::(), + ).subcommand( + "unset-public", + from_fn_async(unset_public) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Allow this interface to infer whether it is publicly addressable based on its IPv4 address") + .with_call_remote::(), + ).subcommand("forget", + from_fn_async(forget_iface) + .with_metadata("sync_db", Value::Bool(true)) + .no_display() + .with_about("Forget a disconnected interface") + .with_call_remote::() + ) +} + +async fn list_interfaces( + ctx: RpcContext, +) -> Result, Error> { + Ok(ctx.net_controller.net_iface.ip_info.read()) +} + +#[derive(Debug, Clone, Deserialize, Serialize, Parser, TS)] +#[ts(export)] +struct NetworkInterfaceSetPublicParams { + #[ts(type = "string")] + interface: InternedString, + public: Option, +} + +async fn set_public( + ctx: RpcContext, + NetworkInterfaceSetPublicParams { interface, public }: NetworkInterfaceSetPublicParams, +) -> Result<(), Error> { + ctx.net_controller + .net_iface + .set_public(&interface, Some(public.unwrap_or(true))) + .await +} + +#[derive(Debug, Clone, Deserialize, Serialize, Parser, TS)] +#[ts(export)] +struct UnsetPublicParams { + #[ts(type = "string")] + interface: InternedString, +} + +async fn unset_public( + ctx: RpcContext, + UnsetPublicParams { interface }: UnsetPublicParams, +) -> Result<(), Error> { + ctx.net_controller + .net_iface + .set_public(&interface, None) + .await +} + +#[derive(Debug, Clone, Deserialize, Serialize, Parser, TS)] +#[ts(export)] +struct ForgetInterfaceParams { + #[ts(type = "string")] + interface: InternedString, +} + +async fn forget_iface( + ctx: RpcContext, + ForgetInterfaceParams { interface }: ForgetInterfaceParams, +) -> Result<(), Error> { + ctx.net_controller.net_iface.forget(&interface).await +} + +#[proxy( + interface = "org.freedesktop.NetworkManager", + default_service = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager" +)] +trait NetworkManager { + #[zbus(property)] + fn all_devices(&self) -> Result, Error>; + + #[zbus(signal)] + fn device_added(&self) -> Result<(), Error>; + + #[zbus(signal)] + fn device_removed(&self) -> Result<(), Error>; + + #[zbus(signal)] + fn state_changed(&self) -> Result<(), Error>; +} + +mod active_connection { + use zbus::proxy; + use zbus::zvariant::OwnedObjectPath; + + use crate::prelude::*; + + #[proxy( + interface = "org.freedesktop.NetworkManager.Connection.Active", + default_service = "org.freedesktop.NetworkManager" + )] + pub trait ActiveConnection { + #[zbus(property)] + fn state_flags(&self) -> Result; + + #[zbus(property, name = "Type")] + fn connection_type(&self) -> Result; + + #[zbus(signal)] + fn state_changed(&self) -> Result<(), Error>; + + #[zbus(property)] + fn dhcp4_config(&self) -> Result; + } +} + +#[proxy( + interface = "org.freedesktop.NetworkManager.IP4Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait Ip4Config { + #[zbus(property)] + fn address_data(&self) -> Result, Error>; +} + +#[proxy( + interface = "org.freedesktop.NetworkManager.IP6Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait Ip6Config { + #[zbus(property)] + fn address_data(&self) -> Result, Error>; +} + +#[derive(Clone, Debug, DeserializeDict, ZValue, ZType)] +#[zvariant(signature = "dict")] +struct AddressData { + address: String, + prefix: u32, +} +impl TryFrom for IpNet { + type Error = Error; + fn try_from(value: AddressData) -> Result { + IpNet::new(value.address.parse()?, value.prefix as u8).with_kind(ErrorKind::ParseNetAddress) + } +} + +#[proxy( + interface = "org.freedesktop.NetworkManager.DHCP4Config", + default_service = "org.freedesktop.NetworkManager" +)] +trait Dhcp4Config { + #[zbus(property)] + fn options(&self) -> Result; +} + +#[derive(Clone, Debug, DeserializeDict, ZType)] +#[zvariant(signature = "dict")] +struct Dhcp4Options { + ntp_servers: Option, +} +impl TryFrom for Dhcp4Options { + type Error = zbus::Error; + fn try_from(value: OwnedValue) -> Result { + let dict = value.downcast_ref::()?; + Ok(Self { + ntp_servers: dict.get::<_, String>(&zbus::zvariant::Str::from_static("ntp_servers"))?, + }) + } +} + +mod device { + use zbus::proxy; + use zbus::zvariant::OwnedObjectPath; + + use crate::prelude::*; + + #[proxy( + interface = "org.freedesktop.NetworkManager.Device", + default_service = "org.freedesktop.NetworkManager" + )] + pub trait Device { + #[zbus(property)] + fn ip_interface(&self) -> Result; + + #[zbus(property)] + fn managed(&self) -> Result; + + #[zbus(property)] + fn active_connection(&self) -> Result; + + #[zbus(property)] + fn ip4_config(&self) -> Result; + + #[zbus(property)] + fn ip6_config(&self) -> Result; + + #[zbus(property, name = "State")] + fn _state(&self) -> Result; + + #[zbus(property)] + fn device_type(&self) -> Result; + + #[zbus(signal)] + fn state_changed(&self) -> Result<(), Error>; + } +} + +trait StubStream<'a> { + fn stub(self) -> impl Stream> + 'a; +} +impl<'a, T> StubStream<'a> for PropertyStream<'a, T> +where + T: Unpin + TryFrom + std::fmt::Debug + 'a, + T::Error: Into, +{ + fn stub(self) -> impl Stream> + 'a { + StreamExt::then(self, |d| async move { + PropertyChanged::get(&d).await.map(|_| ()) + }) + .map_err(Error::from) + } +} +impl<'a> StubStream<'a> for SignalStream<'a> { + fn stub(self) -> impl Stream> + 'a { + self.map(|_| Ok(())) + } +} + +#[instrument(skip_all)] +async fn watcher( + write_to: Watch>, + lxcbr_status: Watch, +) { + loop { + let res: Result<(), Error> = async { + let connection = Connection::system().await?; + + let netman_proxy = NetworkManagerProxy::new(&connection).await?; + + let mut until = Until::new() + .with_stream(netman_proxy.receive_all_devices_changed().await.stub()) + .with_stream( + netman_proxy + .receive_device_added() + .await? + .into_inner() + .stub(), + ) + .with_stream( + netman_proxy + .receive_device_removed() + .await? + .into_inner() + .stub(), + ) + .with_stream( + netman_proxy + .receive_state_changed() + .await? + .into_inner() + .stub(), + ); + + loop { + until + .run(async { + let devices = netman_proxy.all_devices().await?; + let mut ifaces = BTreeSet::new(); + let mut jobs = Vec::new(); + for device in devices { + use futures::future::Either; + + let device_proxy = + device::DeviceProxy::new(&connection, device.clone()).await?; + let iface = InternedString::intern(device_proxy.ip_interface().await?); + if iface.is_empty() { + continue; + } else if &*iface == START9_BRIDGE_IFACE { + jobs.push(Either::Left(watch_activated( + &connection, + device_proxy.clone(), + &lxcbr_status, + ))); + } + + jobs.push(Either::Right(watch_ip( + &connection, + device_proxy.clone(), + iface.clone(), + &write_to, + ))); + ifaces.insert(iface); + } + + write_to.send_if_modified(|m| { + let mut changed = false; + for (iface, info) in m { + if !ifaces.contains(iface) { + info.ip_info = None; + changed = true; + } + } + changed + }); + futures::future::try_join_all(jobs).await?; + + Ok::<_, Error>(()) + }) + .await?; + } + } + .await; + if let Err(e) = res { + tracing::error!("{e}"); + tracing::debug!("{e:?}"); + } + } +} + +async fn get_wan_ipv4(iface: &str) -> Result, Error> { + let client = reqwest::Client::builder(); + #[cfg(target_os = "linux")] + let client = client.interface(iface); + Ok(client + .build()? + .get("http://ip4only.me/api/") + .timeout(Duration::from_secs(10)) + .send() + .await? + .error_for_status()? + .text() + .await? + .split(",") + .skip(1) + .next() + .filter(|s| !s.is_empty()) + .map(|s| s.parse()) + .transpose()?) +} + +#[instrument(skip(connection, device_proxy, write_to))] +async fn watch_ip( + connection: &Connection, + device_proxy: device::DeviceProxy<'_>, + iface: InternedString, + write_to: &Watch>, +) -> Result<(), Error> { + let mut until = Until::new() + .with_stream( + device_proxy + .receive_active_connection_changed() + .await + .stub(), + ) + .with_stream( + device_proxy + .receive_state_changed() + .await? + .into_inner() + .stub(), + ) + .with_stream(device_proxy.receive_ip4_config_changed().await.stub()) + .with_stream(device_proxy.receive_ip6_config_changed().await.stub()) + .with_async_fn(|| { + async { + tokio::time::sleep(Duration::from_secs(300)).await; + Ok(()) + } + .fuse() + }); + + loop { + until + .run(async { + let ip4_config = device_proxy.ip4_config().await?; + let ip6_config = device_proxy.ip6_config().await?; + + let managed = device_proxy.managed().await?; + if !managed { + return Ok(()); + } + let dac = device_proxy.active_connection().await?; + if &*dac == "/" { + return Ok(()); + } + + let active_connection_proxy = + active_connection::ActiveConnectionProxy::new(&connection, dac).await?; + + let mut until = Until::new() + .with_stream( + active_connection_proxy + .receive_state_changed() + .await? + .into_inner() + .stub(), + ) + .with_stream( + active_connection_proxy + .receive_dhcp4_config_changed() + .await + .stub(), + ); + + loop { + until + .run(async { + let external = active_connection_proxy.state_flags().await? & 0x80 != 0; + if external { + return Ok(()); + } + + let device_type = match device_proxy.device_type().await? { + 1 => Some(NetworkInterfaceType::Ethernet), + 2 => Some(NetworkInterfaceType::Wireless), + 29 => Some(NetworkInterfaceType::Wireguard), + _ => None, + }; + + let dhcp4_config = active_connection_proxy.dhcp4_config().await?; + let ip4_proxy = + Ip4ConfigProxy::new(&connection, ip4_config.clone()).await?; + let ip6_proxy = + Ip6ConfigProxy::new(&connection, ip6_config.clone()).await?; + let mut until = Until::new() + .with_stream(ip4_proxy.receive_address_data_changed().await.stub()) + .with_stream(ip6_proxy.receive_address_data_changed().await.stub()); + + let dhcp4_proxy = if &*dhcp4_config != "/" { + let dhcp4_proxy = + Dhcp4ConfigProxy::new(&connection, dhcp4_config).await?; + until = until.with_stream( + dhcp4_proxy.receive_options_changed().await.stub(), + ); + Some(dhcp4_proxy) + } else { + None + }; + + loop { + until + .run(async { + let addresses = ip4_proxy + .address_data() + .await? + .into_iter() + .chain(ip6_proxy.address_data().await?) + .collect_vec(); + let mut ntp_servers = BTreeSet::new(); + if let Some(dhcp4_proxy) = &dhcp4_proxy { + let dhcp = dhcp4_proxy.options().await?; + if let Some(ntp) = dhcp.ntp_servers { + ntp_servers.extend( + ntp.split_whitespace() + .map(InternedString::intern), + ); + } + } + let scope_id = if_nametoindex(&*iface) + .with_kind(ErrorKind::Network)?; + let subnets: BTreeSet = addresses + .into_iter() + .map(TryInto::try_into) + .try_collect()?; + let ip_info = if !subnets.is_empty() { + let wan_ip = match get_wan_ipv4(&*iface).await { + Ok(a) => a, + Err(e) => { + tracing::error!( + "Failed to determine WAN IP for {iface}: {e}" + ); + tracing::debug!("{e:?}"); + None + } + }; + Some(IpInfo { + scope_id, + device_type, + subnets, + wan_ip, + ntp_servers, + }) + } else { + None + }; + + write_to.send_if_modified(|m| { + let public = m.get(&iface).map_or(None, |i| i.public); + m.insert( + iface.clone(), + NetworkInterfaceInfo { + public, + ip_info: ip_info.clone(), + }, + ) + .filter(|old| &old.ip_info == &ip_info) + .is_none() + }); + + Ok::<_, Error>(()) + }) + .await?; + } + }) + .await?; + } + }) + .await?; + } +} + +#[instrument(skip(_connection, device_proxy, write_to))] +async fn watch_activated( + _connection: &Connection, + device_proxy: device::DeviceProxy<'_>, + write_to: &Watch, +) -> Result<(), Error> { + let mut until = Until::new() + .with_stream( + device_proxy + .receive_active_connection_changed() + .await + .stub(), + ) + .with_stream( + device_proxy + .receive_state_changed() + .await? + .into_inner() + .stub(), + ); + + loop { + until + .run(async { + write_to.send(device_proxy._state().await? == 100); + Ok(()) + }) + .await?; + } +} + +pub struct NetworkInterfaceController { + db: TypedPatchDb, + lxcbr_status: Watch, + ip_info: Watch>, + _watcher: NonDetachingJoinHandle<()>, + listeners: SyncMutex>>, +} +impl NetworkInterfaceController { + pub fn lxcbr_status(&self) -> Watch { + self.lxcbr_status.clone_unseen() + } + + pub fn subscribe(&self) -> Watch> { + self.ip_info.clone_unseen() + } + + pub fn ip_info(&self) -> BTreeMap { + self.ip_info.read() + } + + async fn sync( + db: &TypedPatchDb, + info: &BTreeMap, + ) -> Result<(), Error> { + tracing::debug!("syncronizing {info:?} to db"); + + db.mutate(|db| { + db.as_public_mut() + .as_server_info_mut() + .as_network_interfaces_mut() + .ser(info) + }) + .await?; + + let ntp: BTreeSet<_> = info + .values() + .filter_map(|i| i.ip_info.as_ref()) + .flat_map(|i| &i.ntp_servers) + .cloned() + .collect(); + let prev_ntp = tokio_stream::wrappers::LinesStream::new( + BufReader::new(open_file("/etc/systemd/timesyncd.conf").await?).lines(), + ) + .try_filter_map(|l| async move { + Ok(l.strip_prefix("NTP=").map(|s| { + s.split_whitespace() + .map(InternedString::intern) + .collect::>() + })) + }) + .boxed() + .try_next() + .await? + .unwrap_or_default(); + if ntp != prev_ntp { + // sed -i '/\(^\|#\)NTP=/c\NTP='"${servers}" /etc/systemd/timesyncd.conf + Command::new("sed") + .arg("-i") + .arg( + [r#"/\(^\|#\)NTP=/c\NTP="#] + .into_iter() + .chain(Itertools::intersperse( + { + fn to_str(ntp: &InternedString) -> &str { + &*ntp + } + ntp.iter().map(to_str) + }, + " ", + )) + .join(""), + ) + .arg("/etc/systemd/timesyncd.conf") + .invoke(ErrorKind::Filesystem) + .await?; + Command::new("systemctl") + .arg("restart") + .arg("systemd-timesyncd") + .invoke(ErrorKind::Systemd) + .await?; + } + + Ok(()) + } + pub fn new(db: TypedPatchDb) -> Self { + let mut ip_info = Watch::new(BTreeMap::new()); + let lxcbr_status = Watch::new(false); + Self { + db: db.clone(), + lxcbr_status: lxcbr_status.clone(), + ip_info: ip_info.clone(), + _watcher: tokio::spawn(async move { + match db + .peek() + .await + .as_public() + .as_server_info() + .as_network_interfaces() + .de() + { + Ok(mut info) => { + for info in info.values_mut() { + info.ip_info = None; + } + ip_info.send_replace(info); + } + Err(e) => { + tracing::error!("Error loading network interface info: {e}"); + tracing::debug!("{e:?}"); + } + }; + tokio::join!(watcher(ip_info.clone(), lxcbr_status), async { + let res: Result<(), Error> = async { + loop { + if let Err(e) = async { + let ip_info = ip_info.read(); + Self::sync(&db, &ip_info).boxed().await?; + + Ok::<_, Error>(()) + } + .await + { + tracing::error!("Error syncing ip info to db: {e}"); + tracing::debug!("{e:?}"); + } + + let _ = ip_info.changed().await; + } + } + .await; + if let Err(e) = res { + tracing::error!("Error syncing ip info to db: {e}"); + tracing::debug!("{e:?}"); + } + }); + }) + .into(), + listeners: SyncMutex::new(BTreeMap::new()), + } + } + + pub fn bind(&self, port: u16) -> Result { + let arc = Arc::new(()); + self.listeners.mutate(|l| { + if l.get(&port).filter(|w| w.strong_count() > 0).is_some() { + return Err(Error::new( + std::io::Error::from_raw_os_error(libc::EADDRINUSE), + ErrorKind::Network, + )); + } + l.insert(port, Arc::downgrade(&arc)); + Ok(()) + })?; + let ip_info = self.ip_info.clone_unseen(); + Ok(NetworkInterfaceListener { + _arc: arc, + ip_info, + listeners: ListenerMap::new(port), + }) + } + + pub fn upgrade_listener( + &self, + SelfContainedNetworkInterfaceListener { + mut listener, + .. + }: SelfContainedNetworkInterfaceListener, + ) -> Result { + let port = listener.listeners.port; + let arc = &listener._arc; + self.listeners.mutate(|l| { + if l.get(&port).filter(|w| w.strong_count() > 0).is_some() { + return Err(Error::new( + std::io::Error::from_raw_os_error(libc::EADDRINUSE), + ErrorKind::Network, + )); + } + l.insert(port, Arc::downgrade(arc)); + Ok(()) + })?; + let ip_info = self.ip_info.clone_unseen(); + ip_info.mark_changed(); + listener.change_ip_info_source(ip_info); + Ok(listener) + } + + pub async fn set_public( + &self, + interface: &InternedString, + public: Option, + ) -> Result<(), Error> { + let mut sub = self + .db + .subscribe( + "/public/serverInfo/networkInterfaces" + .parse::>() + .with_kind(ErrorKind::Database)?, + ) + .await; + let mut err = None; + let changed = self.ip_info.send_if_modified(|ip_info| { + let prev = std::mem::replace( + &mut match ip_info.get_mut(interface).or_not_found(interface) { + Ok(a) => a, + Err(e) => { + err = Some(e); + return false; + } + } + .public, + public, + ); + prev != public + }); + if let Some(e) = err { + return Err(e); + } + if changed { + sub.recv().await; + } + Ok(()) + } + + pub async fn forget(&self, interface: &InternedString) -> Result<(), Error> { + let mut sub = self + .db + .subscribe( + "/public/serverInfo/networkInterfaces" + .parse::>() + .with_kind(ErrorKind::Database)?, + ) + .await; + let mut err = None; + let changed = self.ip_info.send_if_modified(|ip_info| { + if ip_info + .get(interface) + .map_or(false, |i| i.ip_info.is_some()) + { + err = Some(Error::new( + eyre!("Cannot forget currently connected interface"), + ErrorKind::InvalidRequest, + )); + return false; + } + ip_info.remove(interface).is_some() + }); + if let Some(e) = err { + return Err(e); + } + if changed { + sub.recv().await; + } + Ok(()) + } +} + +struct ListenerMap { + prev_public: bool, + port: u16, + listeners: BTreeMap)>, +} +impl ListenerMap { + fn from_listener(listener: impl IntoIterator) -> Result { + let mut prev_public = false; + let mut port = 0; + let mut listeners = BTreeMap::)>::new(); + for listener in listener { + let mut local = listener.local_addr().with_kind(ErrorKind::Network)?; + if let SocketAddr::V6(l) = &mut local { + if ipv6_is_link_local(*l.ip()) && l.scope_id() == 0 { + continue; // TODO determine scope id + } + } + if port != 0 && port != local.port() { + return Err(Error::new( + eyre!("Provided listeners are bound to different ports"), + ErrorKind::InvalidRequest, + )); + } + let public = match local.ip() { + IpAddr::V4(ip4) => { + !ip4.is_loopback() + && (!ip4.is_private() || ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations + && !ip4.is_link_local() + } + IpAddr::V6(ip6) => !ipv6_is_local(ip6), + }; + prev_public |= public; + port = local.port(); + listeners.insert(local, (listener, public, None)); + } + if port == 0 { + return Err(Error::new( + eyre!("Listener array cannot be empty"), + ErrorKind::InvalidRequest, + )); + } + Ok(Self { + prev_public, + port, + listeners, + }) + } +} +impl ListenerMap { + fn new(port: u16) -> Self { + Self { + prev_public: false, + port, + listeners: BTreeMap::new(), + } + } + + #[instrument(skip(self))] + fn update( + &mut self, + ip_info: &BTreeMap, + public: bool, + ) -> Result<(), Error> { + let mut keep = BTreeSet::::new(); + for info in ip_info.values().chain([&NetworkInterfaceInfo { + public: Some(false), + ip_info: Some(IpInfo { + scope_id: 1, + device_type: None, + subnets: [ + IpNet::new(Ipv4Addr::LOCALHOST.into(), 8).unwrap(), + IpNet::new(Ipv6Addr::LOCALHOST.into(), 128).unwrap(), + ] + .into_iter() + .collect(), + wan_ip: None, + ntp_servers: Default::default(), + }), + }]) { + if public || !info.public() { + if let Some(ip_info) = &info.ip_info { + for ipnet in &ip_info.subnets { + let addr = match ipnet.addr() { + IpAddr::V6(ip6) => SocketAddrV6::new( + ip6, + self.port, + 0, + if ipv6_is_link_local(ip6) { + ip_info.scope_id + } else { + 0 + }, + ) + .into(), + ip => SocketAddr::new(ip, self.port), + }; + keep.insert(addr); + if let Some((_, is_public, wan_ip)) = self.listeners.get_mut(&addr) { + *is_public = info.public(); + *wan_ip = info.ip_info.as_ref().and_then(|i| i.wan_ip); + continue; + } + self.listeners.insert( + addr, + ( + TcpListener::from_std( + mio::net::TcpListener::bind(addr) + .with_ctx(|_| { + ( + ErrorKind::Network, + lazy_format!("binding to {addr:?}"), + ) + })? + .into(), + ) + .with_kind(ErrorKind::Network)?, + info.public(), + info.ip_info.as_ref().and_then(|i| i.wan_ip), + ), + ); + } + } + } + } + self.listeners.retain(|key, _| keep.contains(key)); + self.prev_public = public; + Ok(()) + } + fn poll_accept(&self, cx: &mut std::task::Context<'_>) -> Poll> { + for (bind_addr, listener) in self.listeners.iter() { + if let Poll::Ready((stream, addr)) = listener.0.poll_accept(cx)? { + return Poll::Ready(Ok(Accepted { + stream, + peer: addr, + is_public: listener.1, + wan_ip: listener.2, + bind: *bind_addr, + })); + } + } + Poll::Pending + } +} + +pub struct NetworkInterfaceListener { + ip_info: Watch>, + listeners: ListenerMap, + _arc: Arc<()>, +} +impl NetworkInterfaceListener { + pub fn port(&self) -> u16 { + self.listeners.port + } + + pub fn poll_accept( + &mut self, + cx: &mut std::task::Context<'_>, + public: bool, + ) -> Poll> { + while self.ip_info.poll_changed(cx).is_ready() || public != self.listeners.prev_public { + self.ip_info + .peek(|ip_info| self.listeners.update(ip_info, public))?; + } + self.listeners.poll_accept(cx) + } + + pub(super) fn new( + mut ip_info: Watch>, + port: u16, + ) -> Self { + ip_info.mark_unseen(); + Self { + ip_info, + listeners: ListenerMap::new(port), + _arc: Arc::new(()), + } + } + + pub fn change_ip_info_source( + &mut self, + mut ip_info: Watch>, + ) { + ip_info.mark_unseen(); + self.ip_info = ip_info; + } + + pub async fn accept(&mut self, public: bool) -> Result { + futures::future::poll_fn(|cx| self.poll_accept(cx, public)).await + } +} + +pub struct Accepted { + pub stream: TcpStream, + pub peer: SocketAddr, + pub is_public: bool, + pub wan_ip: Option, + pub bind: SocketAddr, +} + +pub struct SelfContainedNetworkInterfaceListener { + _watch_thread: NonDetachingJoinHandle<()>, + listener: NetworkInterfaceListener, +} +impl SelfContainedNetworkInterfaceListener { + pub fn bind(port: u16) -> Self { + let ip_info = Watch::new(BTreeMap::new()); + let _watch_thread = tokio::spawn(watcher(ip_info.clone(), Watch::new(false))).into(); + Self { + _watch_thread, + listener: NetworkInterfaceListener::new(ip_info, port), + } + } +} +impl Accept for SelfContainedNetworkInterfaceListener { + fn poll_accept( + &mut self, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Accept::poll_accept(&mut self.listener, cx) + } +} diff --git a/core/startos/src/net/service_interface.rs b/core/startos/src/net/service_interface.rs index ade10d959..ad2900da7 100644 --- a/core/startos/src/net/service_interface.rs +++ b/core/startos/src/net/service_interface.rs @@ -12,7 +12,8 @@ use ts_rs::TS; #[serde(tag = "kind")] pub enum HostnameInfo { Ip { - network_interface_id: String, + #[ts(type = "string")] + network_interface_id: InternedString, public: bool, hostname: IpHostname, }, @@ -43,6 +44,8 @@ pub enum IpHostname { }, Ipv6 { value: Ipv6Addr, + #[serde(default)] + scope_id: u32, port: Option, ssl_port: Option, }, @@ -69,7 +72,6 @@ pub struct ServiceInterface { pub id: ServiceInterfaceId, pub name: String, pub description: String, - pub has_primary: bool, pub masked: bool, pub address_info: AddressInfo, #[serde(rename = "type")] diff --git a/core/startos/src/net/ssl.rs b/core/startos/src/net/ssl.rs index 29bcd9652..a89853591 100644 --- a/core/startos/src/net/ssl.rs +++ b/core/startos/src/net/ssl.rs @@ -17,7 +17,6 @@ use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509}; use openssl::*; use patch_db::HasModel; use serde::{Deserialize, Serialize}; -use tokio::time::Instant; use tracing::instrument; use crate::account::AccountInfo; diff --git a/core/startos/src/net/static_server.rs b/core/startos/src/net/static_server.rs index c070d7920..d1070f9e1 100644 --- a/core/startos/src/net/static_server.rs +++ b/core/startos/src/net/static_server.rs @@ -8,15 +8,15 @@ use std::time::UNIX_EPOCH; use async_compression::tokio::bufread::GzipEncoder; use axum::body::Body; use axum::extract::{self as x, Request}; -use axum::response::Response; -use axum::routing::{any, get, post}; +use axum::response::{Redirect, Response}; +use axum::routing::{any, get}; use axum::Router; use base64::display::Base64Display; use digest::Digest; use futures::future::ready; use http::header::{ ACCEPT_ENCODING, ACCEPT_RANGES, CACHE_CONTROL, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, - CONTENT_RANGE, CONTENT_TYPE, ETAG, RANGE, + CONTENT_RANGE, CONTENT_TYPE, ETAG, HOST, RANGE, }; use http::request::Parts as RequestParts; use http::{HeaderValue, Method, StatusCode}; @@ -26,7 +26,6 @@ use new_mime_guess::MimeGuess; use openssl::hash::MessageDigest; use openssl::x509::X509; use rpc_toolkit::{Context, HttpServer, Server}; -use sqlx::query; use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt, BufReader}; use tokio_util::io::ReaderStream; use url::Url; @@ -47,7 +46,7 @@ use crate::s9pk::S9pk; use crate::util::io::open_file; use crate::util::net::SyncBody; use crate::util::serde::BASE64; -use crate::{diagnostic_api, init_api, install_api, main_api, setup_api}; +use crate::{diagnostic_api, init_api, install_api, main_api, setup_api, DATA_DIR}; const NOT_FOUND: &[u8] = b"Not Found"; const METHOD_NOT_ALLOWED: &[u8] = b"Method Not Allowed"; @@ -230,6 +229,20 @@ pub fn refresher() -> Router { })) } +pub fn redirecter() -> Router { + Router::new().fallback(get(|request: Request| async move { + Redirect::temporary(&format!( + "https://{}{}", + request + .headers() + .get(HOST) + .and_then(|s| s.to_str().ok()) + .unwrap_or("localhost"), + request.uri() + )) + })) +} + async fn proxy_request(ctx: RpcContext, request: Request, url: String) -> Result { if_authorized(&ctx, request, |mut request| async { for header in PROXY_STRIP_HEADERS { @@ -253,7 +266,7 @@ fn s9pk_router(ctx: RpcContext) -> Router { let (parts, _) = request.into_parts(); match FileData::from_path( &parts, - &ctx.datadir + &Path::new(DATA_DIR) .join(PKG_ARCHIVE_DIR) .join("installed") .join(s9pk), @@ -279,7 +292,7 @@ fn s9pk_router(ctx: RpcContext) -> Router { let s9pk = S9pk::deserialize( &MultiCursorFile::from( open_file( - ctx.datadir + Path::new(DATA_DIR) .join(PKG_ARCHIVE_DIR) .join("installed") .join(s9pk), diff --git a/core/startos/src/net/utils.rs b/core/startos/src/net/utils.rs index 9cba8a0cd..8799fd6cd 100644 --- a/core/startos/src/net/utils.rs +++ b/core/startos/src/net/utils.rs @@ -1,16 +1,32 @@ -use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; use std::path::Path; use async_stream::try_stream; use color_eyre::eyre::eyre; use futures::stream::BoxStream; use futures::{StreamExt, TryStreamExt}; -use ipnet::{Ipv4Net, Ipv6Net}; +use helpers::NonDetachingJoinHandle; +use imbl_value::InternedString; +use ipnet::{IpNet, Ipv4Net, Ipv6Net}; +use nix::net::if_::if_nametoindex; use tokio::net::{TcpListener, TcpStream}; use tokio::process::Command; +use crate::db::model::public::NetworkInterfaceInfo; +use crate::net::network_interface::NetworkInterfaceListener; +use crate::net::web_server::Accept; +use crate::prelude::*; +use crate::util::sync::Watch; use crate::util::Invoke; -use crate::Error; + +pub fn ipv6_is_link_local(addr: Ipv6Addr) -> bool { + (addr.segments()[0] & 0xffc0) == 0xfe80 +} + +pub fn ipv6_is_local(addr: Ipv6Addr) -> bool { + addr.is_loopback() || (addr.segments()[0] & 0xfe00) == 0xfc00 || ipv6_is_link_local(addr) +} fn parse_iface_ip(output: &str) -> Result, Error> { let output = output.trim(); @@ -112,6 +128,55 @@ pub async fn find_eth_iface() -> Result { )) } +pub async fn all_socket_addrs_for(port: u16) -> Result, Error> { + let mut res = Vec::new(); + + let raw = String::from_utf8( + Command::new("ip") + .arg("-o") + .arg("addr") + .arg("show") + .invoke(ErrorKind::ParseSysInfo) + .await?, + )?; + let err = |item: &str, lineno: usize, line: &str| { + Error::new( + eyre!("failed to parse ip info ({item}[line:{lineno}]) from {line:?}"), + ErrorKind::ParseSysInfo, + ) + }; + for (idx, line) in raw + .lines() + .map(|l| l.trim()) + .enumerate() + .filter(|(_, l)| !l.is_empty()) + { + let mut split = line.split_whitespace(); + let _num = split.next(); + let ifname = split.next().ok_or_else(|| err("ifname", idx, line))?; + let _kind = split.next(); + let ipnet_str = split.next().ok_or_else(|| err("ipnet", idx, line))?; + let ipnet = ipnet_str + .parse::() + .with_ctx(|_| (ErrorKind::ParseSysInfo, err("ipnet", idx, ipnet_str)))?; + match ipnet.addr() { + IpAddr::V4(ip4) => res.push((ifname.into(), SocketAddr::new(ip4.into(), port))), + IpAddr::V6(ip6) => res.push(( + ifname.into(), + SocketAddr::V6(SocketAddrV6::new( + ip6, + port, + 0, + if_nametoindex(ifname) + .with_ctx(|_| (ErrorKind::ParseSysInfo, "reading scope_id"))?, + )), + )), + } + } + + Ok(res) +} + pub struct TcpListeners { listeners: Vec, } diff --git a/core/startos/src/net/vhost.rs b/core/startos/src/net/vhost.rs index 7d48b1469..cbc1cc916 100644 --- a/core/startos/src/net/vhost.rs +++ b/core/startos/src/net/vhost.rs @@ -1,22 +1,23 @@ -use std::collections::BTreeMap; -use std::net::{IpAddr, Ipv6Addr, SocketAddr}; -use std::str::FromStr; +use std::collections::{BTreeMap, BTreeSet}; +use std::net::{IpAddr, SocketAddr}; use std::sync::{Arc, Weak}; use std::time::Duration; -use async_acme::acme::ACME_TLS_ALPN_NAME; +use async_acme::acme::{Identifier, ACME_TLS_ALPN_NAME}; use axum::body::Body; use axum::extract::Request; use axum::response::Response; use color_eyre::eyre::eyre; +use futures::FutureExt; use helpers::NonDetachingJoinHandle; use http::Uri; use imbl_value::InternedString; use models::ResultExt; +use rpc_toolkit::{from_fn, Context, HandlerArgs, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; use tokio::io::AsyncWriteExt; -use tokio::net::{TcpListener, TcpStream}; -use tokio::sync::{watch, Mutex, RwLock}; +use tokio::net::TcpStream; +use tokio::sync::watch; use tokio_rustls::rustls::crypto::CryptoProvider; use tokio_rustls::rustls::pki_types::{ CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, @@ -30,13 +31,60 @@ use tokio_stream::StreamExt; use tracing::instrument; use ts_rs::TS; +use crate::context::{CliContext, RpcContext}; use crate::db::model::Database; -use crate::net::acme::AcmeCertCache; +use crate::net::acme::{AcmeCertCache, AcmeProvider}; +use crate::net::network_interface::{ + Accepted, NetworkInterfaceController, NetworkInterfaceListener, +}; use crate::net::static_server::server_error; use crate::prelude::*; use crate::util::io::BackTrackingIO; +use crate::util::serde::{display_serializable, HandlerExtSerde, MaybeUtf8String}; use crate::util::sync::SyncMutex; -use crate::util::serde::MaybeUtf8String; + +pub fn vhost_api() -> ParentHandler { + ParentHandler::new().subcommand( + "dump-table", + from_fn(|ctx: RpcContext| Ok(ctx.net_controller.vhost.dump_table())) + .with_display_serializable() + .with_custom_display_fn(|HandlerArgs { params, .. }, res| { + use prettytable::*; + + if let Some(format) = params.format { + display_serializable(format, res); + return Ok::<_, Error>(()); + } + + let mut table = Table::new(); + table.add_row(row![bc => "FROM", "TO", "PUBLIC", "ACME", "CONNECT SSL", "ACTIVE"]); + + for (external, targets) in res { + for (host, targets) in targets { + for (idx, target) in targets.into_iter().enumerate() { + table.add_row(row![ + format!( + "{}:{}", + host.as_ref().map(|s| &**s).unwrap_or("*"), + external.0 + ), + target.addr, + target.public, + target.acme.as_ref().map(|a| a.0.as_str()).unwrap_or("NONE"), + target.connect_ssl.is_ok(), + idx == 0 + ]); + } + } + } + + table.print_tty(false)?; + + Ok(()) + }) + .with_call_remote::(), + ) +} #[derive(Debug)] struct SingleCertResolver(Arc); @@ -49,62 +97,108 @@ impl ResolvesServerCert for SingleCertResolver { // not allowed: <=1024, >=32768, 5355, 5432, 9050, 6010, 9051, 5353 pub struct VHostController { - crypto_provider: Arc, db: TypedPatchDb, - servers: Mutex>, + interfaces: Arc, + crypto_provider: Arc, + acme_tls_alpn_cache: AcmeTlsAlpnCache, + servers: SyncMutex>, } impl VHostController { - pub fn new(db: TypedPatchDb) -> Self { + pub fn new(db: TypedPatchDb, interfaces: Arc) -> Self { Self { - crypto_provider: Arc::new(tokio_rustls::rustls::crypto::ring::default_provider()), db, - servers: Mutex::new(BTreeMap::new()), + interfaces, + crypto_provider: Arc::new(tokio_rustls::rustls::crypto::ring::default_provider()), + acme_tls_alpn_cache: Arc::new(SyncMutex::new(BTreeMap::new())), + servers: SyncMutex::new(BTreeMap::new()), } } #[instrument(skip_all)] - pub async fn add( + pub fn add( &self, hostname: Option, external: u16, - target: SocketAddr, - connect_ssl: Result<(), AlpnInfo>, // Ok: yes, connect using ssl, pass through alpn; Err: connect tcp, use provided strategy for alpn + TargetInfo { + public, + acme, + addr, + connect_ssl, + }: TargetInfo, ) -> Result, Error> { - let mut writable = self.servers.lock().await; - let server = if let Some(server) = writable.remove(&external) { - server - } else { - tracing::info!("Listening on {external}"); - VHostServer::new(external, self.db.clone(), self.crypto_provider.clone()).await? - }; - let rc = server - .add( + self.servers.mutate(|writable| { + let server = if let Some(server) = writable.remove(&external) { + server + } else { + VHostServer::new( + external, + self.db.clone(), + self.interfaces.clone(), + self.crypto_provider.clone(), + self.acme_tls_alpn_cache.clone(), + )? + }; + let rc = server.add( hostname, TargetInfo { - addr: target, + public, + acme, + addr, connect_ssl, }, - ) - .await; - writable.insert(external, server); - Ok(rc?) + ); + writable.insert(external, server); + Ok(rc?) + }) } + + pub fn dump_table( + &self, + ) -> BTreeMap, BTreeMap>, BTreeSet>> + { + self.servers.peek(|s| { + s.iter() + .map(|(k, v)| { + ( + JsonKey::new(*k), + v.mapping + .borrow() + .iter() + .map(|(k, v)| { + ( + JsonKey::new(k.clone()), + v.iter() + .filter(|(_, v)| v.strong_count() > 0) + .map(|(k, _)| k) + .cloned() + .collect(), + ) + }) + .collect(), + ) + }) + .collect() + }) + } + #[instrument(skip_all)] - pub async fn gc(&self, hostname: Option, external: u16) -> Result<(), Error> { - let mut writable = self.servers.lock().await; - if let Some(server) = writable.remove(&external) { - server.gc(hostname).await?; - if !server.is_empty().await? { - writable.insert(external, server); + pub fn gc(&self, hostname: Option, external: u16) { + self.servers.mutate(|writable| { + if let Some(server) = writable.remove(&external) { + server.gc(hostname); + if !server.is_empty() { + writable.insert(external, server); + } } - } - Ok(()) + }) } } -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] -struct TargetInfo { - addr: SocketAddr, - connect_ssl: Result<(), AlpnInfo>, +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct TargetInfo { + pub public: bool, + pub acme: Option, + pub addr: SocketAddr, + pub connect_ssl: Result<(), AlpnInfo>, // Ok: yes, connect using ssl, pass through alpn; Err: connect tcp, use provided strategy for alpn } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, TS)] @@ -120,421 +214,504 @@ impl Default for AlpnInfo { } } +type AcmeTlsAlpnCache = + Arc>>>>>; +type Mapping = BTreeMap, BTreeMap>>; + struct VHostServer { - mapping: Weak, BTreeMap>>>>, + mapping: watch::Sender, _thread: NonDetachingJoinHandle<()>, } + impl VHostServer { - #[instrument(skip_all)] - async fn new(port: u16, db: TypedPatchDb, crypto_provider: Arc) -> Result { - let acme_tls_alpn_cache = Arc::new(SyncMutex::new(BTreeMap::< - InternedString, - watch::Receiver>>, - >::new())); - // check if port allowed - let listener = TcpListener::bind(SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), port)) - .await - .with_kind(crate::ErrorKind::Network)?; - let mapping = Arc::new(RwLock::new(BTreeMap::new())); - Ok(Self { - mapping: Arc::downgrade(&mapping), - _thread: tokio::spawn(async move { - loop { - match listener.accept().await { - Ok((stream, _)) => { - 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:?}"); - } + async fn accept( + listener: &mut NetworkInterfaceListener, + mut mapping: watch::Receiver, + db: TypedPatchDb, + acme_tls_alpn_cache: AcmeTlsAlpnCache, + crypto_provider: Arc, + ) -> Result<(), Error> { + let accepted; - let mut stream = BackTrackingIO::new(stream); - let mapping = mapping.clone(); - let db = db.clone(); - let acme_tls_alpn_cache = acme_tls_alpn_cache.clone(); - let crypto_provider = crypto_provider.clone(); - tokio::spawn(async move { - if let Err(e) = async { - let mid: tokio_rustls::StartHandshake<&mut BackTrackingIO> = match LazyConfigAcceptor::new( - Acceptor::default(), - &mut stream, - ) - .await - { - Ok(a) => a, - Err(_) => { - 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.scheme = Some("https".parse()?); - 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 = - mid.client_hello().server_name().map(|s| s.into()); - let target = { - let mapping = mapping.read().await; - mapping - .get(&target_name) - .into_iter() - .flatten() - .find(|(_, rc)| rc.strong_count() > 0) - .or_else(|| { - if target_name - .as_ref() - .map(|s| s.parse::().is_ok()) - .unwrap_or(true) - { - mapping - .get(&None) - .into_iter() - .flatten() - .find(|(_, rc)| rc.strong_count() > 0) - } else { - None - } - }) - .map(|(target, _)| target.clone()) - }; - if let Some(target) = target { - let peek = db.peek().await; - let root = peek.as_private().as_key_store().as_local_certs().as_root_cert().de()?; - let mut cfg = match async { - if let Some(acme_settings) = peek.as_public().as_server_info().as_acme().de()? { - if let Some(domain) = target_name.as_ref().filter(|target_name| acme_settings.domains.contains(*target_name)) { - if mid - .client_hello() - .alpn() - .into_iter() - .flatten() - .any(|alpn| alpn == ACME_TLS_ALPN_NAME) - { - let cert = WatchStream::new( - acme_tls_alpn_cache.peek(|c| c.get(&**domain).cloned()) - .ok_or_else(|| { - Error::new( - eyre!("No challenge recv available for {domain}"), - ErrorKind::OpenSsl - ) - })?, - ); - tracing::info!("Waiting for verification cert for {domain}"); - let cert = cert - .filter(|c| c.is_some()) - .next() - .await - .flatten() - .ok_or_else(|| { - Error::new(eyre!("No challenge available for {domain}"), ErrorKind::OpenSsl) - })?; - tracing::info!("Verification cert received for {domain}"); - let mut cfg = ServerConfig::builder_with_provider(crypto_provider.clone()) - .with_safe_default_protocol_versions() - .with_kind(crate::ErrorKind::OpenSsl)? - .with_no_client_auth() - .with_cert_resolver(Arc::new(SingleCertResolver(cert))); + loop { + let any_public = mapping + .borrow() + .iter() + .any(|(_, targets)| targets.iter().any(|(target, _)| target.public)); - cfg.alpn_protocols = vec![ACME_TLS_ALPN_NAME.to_vec()]; - return Ok(Err(cfg)); - } else { - let domains = [domain.to_string()]; - let (send, recv) = watch::channel(None); - acme_tls_alpn_cache.mutate(|c| c.insert(domain.clone(), recv)); - let cert = - async_acme::rustls_helper::order( - |_, cert| { - send.send_replace(Some(Arc::new(cert))); - Ok(()) - }, - acme_settings.provider.as_str(), - &domains, - Some(&AcmeCertCache(&db)), - &acme_settings.contact, - ) - .await - .with_kind(ErrorKind::OpenSsl)?; - return Ok(Ok( - ServerConfig::builder_with_provider(crypto_provider.clone()) - .with_safe_default_protocol_versions() - .with_kind(crate::ErrorKind::OpenSsl)? - .with_no_client_auth() - .with_cert_resolver(Arc::new(SingleCertResolver(Arc::new(cert)))) - )); - } - } - } - let hostnames = target_name - .into_iter() - .chain( - peek - .as_public() - .as_server_info() - .as_ip_info() - .as_entries()? - .into_iter() - .flat_map(|(_, ips)| [ - ips.as_ipv4().de().map(|ip| ip.map(IpAddr::V4)), - ips.as_ipv6().de().map(|ip| ip.map(IpAddr::V6)) - ]) - .filter_map(|a| a.transpose()) - .map(|a| a.map(|ip| InternedString::from_display(&ip))) - .collect::, _>>()?, - ) - .collect(); - let key = db - .mutate(|v| { - v.as_private_mut() - .as_key_store_mut() - .as_local_certs_mut() - .cert_for(&hostnames) - }) - .await?; - let cfg = ServerConfig::builder_with_provider(crypto_provider.clone()) - .with_safe_default_protocol_versions() - .with_kind(crate::ErrorKind::OpenSsl)? - .with_no_client_auth(); - if mid.client_hello().signature_schemes().contains( - &tokio_rustls::rustls::SignatureScheme::ED25519, - ) { - cfg.with_single_cert( - key.fullchain_ed25519() - .into_iter() - .map(|c| { - Ok(tokio_rustls::rustls::pki_types::CertificateDer::from( - c.to_der()?, - )) - }) - .collect::>()?, - PrivateKeyDer::from(PrivatePkcs8KeyDer::from( - key.leaf - .keys - .ed25519 - .private_key_to_pkcs8()?, - )), - ) + let changed_public = mapping + .wait_for(|m| { + m.iter() + .any(|(_, targets)| targets.iter().any(|(target, _)| target.public)) + != any_public + }) + .boxed(); + + tokio::select! { + a = listener.accept(any_public) => { + accepted = a?; + break; + } + _ = changed_public => { + tracing::debug!("port {} {} public bindings", listener.port(), if any_public { "no longer has" } else { "now has" }); + } + } + } + + if let Err(e) = socket2::SockRef::from(&accepted.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:?}"); + } + + tokio::spawn(async move { + let bind = accepted.bind; + if let Err(e) = + Self::handle_stream(accepted, mapping, db, acme_tls_alpn_cache, crypto_provider) + .await + { + tracing::error!("Error in VHostController on {bind}: {e}"); + tracing::debug!("{e:?}") + } + }); + Ok(()) + } + + async fn handle_stream( + Accepted { + stream, + is_public, + wan_ip, + bind, + .. + }: Accepted, + mapping: watch::Receiver, + db: TypedPatchDb, + acme_tls_alpn_cache: AcmeTlsAlpnCache, + crypto_provider: Arc, + ) -> Result<(), Error> { + let mut stream = BackTrackingIO::new(stream); + let mid: tokio_rustls::StartHandshake<&mut BackTrackingIO> = + match LazyConfigAcceptor::new(Acceptor::default(), &mut stream).await { + Ok(a) => a, + Err(e) => { + let (_, buf) = stream.rewind(); + if std::str::from_utf8(buf) + .ok() + .and_then(|buf| { + buf.lines() + .map(|l| l.trim()) + .filter(|l| !l.is_empty()) + .next() + }) + .map_or(false, |buf| { + regex::Regex::new("[A-Z]+ (.+) HTTP/1") + .unwrap() + .is_match(buf) + }) + { + 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()); + if let Some(host) = host { + let uri = Uri::from_parts({ + let mut parts = + req.uri().to_owned().into_parts(); + parts.scheme = Some("https".parse()?); + parts.authority = Some(host.parse()?); + parts + })?; + Response::builder() + .status(http::StatusCode::TEMPORARY_REDIRECT) + .header(http::header::LOCATION, uri.to_string()) + .body(Body::default()) } else { - cfg.with_single_cert( - key.fullchain_nistp256() - .into_iter() - .map(|c| { - Ok(tokio_rustls::rustls::pki_types::CertificateDer::from( - c.to_der()?, - )) - }) - .collect::>()?, - PrivateKeyDer::from(PrivatePkcs8KeyDer::from( - key.leaf - .keys - .nistp256 - .private_key_to_pkcs8()?, - )), - ) - } - .with_kind(crate::ErrorKind::OpenSsl) - .map(Ok) - }.await? { - Ok(a) => a, - Err(cfg) => { - tracing::info!("performing ACME auth challenge"); - let mut accept = mid.into_stream(Arc::new(cfg)); - let io = accept.get_mut().unwrap(); - let buffered = io.stop_buffering(); - io.write_all(&buffered).await?; - accept.await?; - tracing::info!("ACME auth challenge completed"); - return Ok(()); - } - }; - let mut tcp_stream = - TcpStream::connect(target.addr).await?; - match target.connect_ssl { - Ok(()) => { - let mut client_cfg = - tokio_rustls::rustls::ClientConfig::builder_with_provider(crypto_provider) - .with_safe_default_protocol_versions() - .with_kind(crate::ErrorKind::OpenSsl)? - .with_root_certificates({ - let mut store = RootCertStore::empty(); - store.add( - CertificateDer::from( - root.to_der()?, - ), - ).with_kind(crate::ErrorKind::OpenSsl)?; - store - }) - .with_no_client_auth(); - client_cfg.alpn_protocols = mid - .client_hello() - .alpn() - .into_iter() - .flatten() - .map(|x| x.to_vec()) - .collect(); - let mut target_stream = - TlsConnector::from(Arc::new(client_cfg)) - .connect_with( - ServerName::IpAddress( - target.addr.ip().into(), - ), - tcp_stream, - |conn| { - cfg.alpn_protocols.extend( - conn.alpn_protocol() - .into_iter() - .map(|p| p.to_vec()), - ) - }, - ) - .await - .with_kind(crate::ErrorKind::OpenSsl)?; - let mut accept = mid.into_stream(Arc::new(cfg)); - let io = accept.get_mut().unwrap(); - let buffered = io.stop_buffering(); - io.write_all(&buffered).await?; - let mut tls_stream = - match accept.await { - Ok(a) => a, - Err(e) => { - tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}"); - tracing::trace!("{e:?}"); - return Ok(()) - } - }; - tokio::io::copy_bidirectional( - &mut tls_stream, - &mut target_stream, - ) - .await - } - Err(AlpnInfo::Reflect) => { - for proto in - mid.client_hello().alpn().into_iter().flatten() - { - cfg.alpn_protocols.push(proto.into()); - } - let mut accept = mid.into_stream(Arc::new(cfg)); - let io = accept.get_mut().unwrap(); - let buffered = io.stop_buffering(); - io.write_all(&buffered).await?; - let mut tls_stream = - match accept.await { - Ok(a) => a, - Err(e) => { - tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}"); - tracing::trace!("{e:?}"); - return Ok(()) - } - }; - tokio::io::copy_bidirectional( - &mut tls_stream, - &mut tcp_stream, - ) - .await - } - Err(AlpnInfo::Specified(alpn)) => { - cfg.alpn_protocols = alpn.into_iter().map(|a| a.0).collect(); - let mut accept = mid.into_stream(Arc::new(cfg)); - let io = accept.get_mut().unwrap(); - let buffered = io.stop_buffering(); - io.write_all(&buffered).await?; - let mut tls_stream = - match accept.await { - Ok(a) => a, - Err(e) => { - tracing::trace!( "VHostController: failed to accept TLS connection on port {port}: {e}"); - tracing::trace!("{e:?}"); - return Ok(()) - } - }; - tokio::io::copy_bidirectional( - &mut tls_stream, - &mut tcp_stream, - ) - .await + Response::builder() + .status(http::StatusCode::BAD_REQUEST) + .body(Body::from("Host header required")) } } - .map_or_else( - |e| { - use std::io::ErrorKind as E; - match e.kind() { - E::UnexpectedEof | E::BrokenPipe | E::ConnectionAborted | E::ConnectionReset | E::ConnectionRefused | E::TimedOut | E::Interrupted | E::NotConnected => Ok(()), - _ => Err(e), - }}, - |_| Ok(()), - )?; - } else { - // 503 - } - Ok::<_, Error>(()) - } - .await - { - tracing::error!("Error in VHostController on port {port}: {e}"); - tracing::debug!("{e:?}") - } - }); + .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) + }); + } else { + return Err(e).with_kind(ErrorKind::Network); + } + } + }; + let target_name: Option = + mid.client_hello().server_name().map(|s| s.into()); + if let Some(domain) = target_name.as_ref() { + if mid + .client_hello() + .alpn() + .into_iter() + .flatten() + .any(|alpn| alpn == ACME_TLS_ALPN_NAME) + { + let cert = WatchStream::new( + acme_tls_alpn_cache + .peek(|c| c.get(&**domain).cloned()) + .ok_or_else(|| { + Error::new( + eyre!("No challenge recv available for {domain}"), + ErrorKind::OpenSsl, + ) + })?, + ); + tracing::info!("Waiting for verification cert for {domain}"); + let cert = cert + .filter(|c| c.is_some()) + .next() + .await + .flatten() + .ok_or_else(|| { + Error::new( + eyre!("No challenge available for {domain}"), + ErrorKind::OpenSsl, + ) + })?; + tracing::info!("Verification cert received for {domain}"); + let mut cfg = ServerConfig::builder_with_provider(crypto_provider.clone()) + .with_safe_default_protocol_versions() + .with_kind(crate::ErrorKind::OpenSsl)? + .with_no_client_auth() + .with_cert_resolver(Arc::new(SingleCertResolver(cert))); + + cfg.alpn_protocols = vec![ACME_TLS_ALPN_NAME.to_vec()]; + tracing::info!("performing ACME auth challenge"); + let mut accept = mid.into_stream(Arc::new(cfg)); + let io = accept.get_mut().unwrap(); + let buffered = io.stop_buffering(); + io.write_all(&buffered).await?; + accept.await?; + tracing::info!("ACME auth challenge completed"); + return Ok(()); + } + } + let target = { + let m = mapping.borrow(); + m.get(&target_name) + .into_iter() + .flatten() + .find(|(_, rc)| rc.strong_count() > 0) + .or_else(|| { + if target_name + .as_ref() + .map(|s| s.parse::().is_ok()) + .unwrap_or(true) + { + m.get(&None) + .into_iter() + .flatten() + .find(|(_, rc)| rc.strong_count() > 0) + } else { + None + } + }) + .map(|(target, _)| target.clone()) + }; + if let Some(target) = target { + if is_public && !target.public { + log::warn!( + "Rejecting connection from public interface to private bind: {bind} -> {target:?}" + ); + return Ok(()); + } + let peek = db.peek().await; + let root = peek + .as_private() + .as_key_store() + .as_local_certs() + .as_root_cert() + .de()?; + let mut cfg = async { + if let Some((domain, provider, settings)) = + target_name.as_ref().and_then(|domain| { + target.acme.as_ref().and_then(|a| { + peek.as_public() + .as_server_info() + .as_acme() + .as_idx(a) + .map(|s| (domain, a, s)) + }) + }) + { + let acme_settings = settings.de()?; + let mut identifiers = vec![Identifier::Dns(domain.to_string())]; + if false + // Requires RFC 8738 + { + if let Some(wan_ip) = wan_ip { + identifiers.push(Identifier::Ip(wan_ip.into())); } + } + let (send, recv) = watch::channel(None); + acme_tls_alpn_cache.mutate(|c| c.insert(domain.clone(), recv)); + let cert = async_acme::rustls_helper::order( + |_, cert| { + send.send_replace(Some(Arc::new(cert))); + Ok(()) + }, + provider.0.as_str(), + &identifiers, + Some(&AcmeCertCache(&db)), + &acme_settings.contact, + ) + .await + .with_kind(ErrorKind::OpenSsl)?; + return Ok(ServerConfig::builder_with_provider(crypto_provider.clone()) + .with_safe_default_protocol_versions() + .with_kind(crate::ErrorKind::OpenSsl)? + .with_no_client_auth() + .with_cert_resolver(Arc::new(SingleCertResolver(Arc::new(cert))))); + } + + let hostnames = target_name + .into_iter() + .chain([InternedString::from_display(&bind.ip())]) + .chain(wan_ip.as_ref().map(InternedString::from_display)) + .collect(); + let key = db + .mutate(|v| { + v.as_private_mut() + .as_key_store_mut() + .as_local_certs_mut() + .cert_for(&hostnames) + }) + .await?; + let cfg = ServerConfig::builder_with_provider(crypto_provider.clone()) + .with_safe_default_protocol_versions() + .with_kind(crate::ErrorKind::OpenSsl)? + .with_no_client_auth(); + if mid + .client_hello() + .signature_schemes() + .contains(&tokio_rustls::rustls::SignatureScheme::ED25519) + { + cfg.with_single_cert( + key.fullchain_ed25519() + .into_iter() + .map(|c| { + Ok(tokio_rustls::rustls::pki_types::CertificateDer::from( + c.to_der()?, + )) + }) + .collect::>()?, + PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + key.leaf.keys.ed25519.private_key_to_pkcs8()?, + )), + ) + } else { + cfg.with_single_cert( + key.fullchain_nistp256() + .into_iter() + .map(|c| { + Ok(tokio_rustls::rustls::pki_types::CertificateDer::from( + c.to_der()?, + )) + }) + .collect::>()?, + PrivateKeyDer::from(PrivatePkcs8KeyDer::from( + key.leaf.keys.nistp256.private_key_to_pkcs8()?, + )), + ) + } + .with_kind(crate::ErrorKind::OpenSsl) + } + .await?; + let mut tcp_stream = TcpStream::connect(target.addr).await?; + match target.connect_ssl { + Ok(()) => { + let mut client_cfg = + tokio_rustls::rustls::ClientConfig::builder_with_provider(crypto_provider) + .with_safe_default_protocol_versions() + .with_kind(crate::ErrorKind::OpenSsl)? + .with_root_certificates({ + let mut store = RootCertStore::empty(); + store + .add(CertificateDer::from(root.to_der()?)) + .with_kind(crate::ErrorKind::OpenSsl)?; + store + }) + .with_no_client_auth(); + client_cfg.alpn_protocols = mid + .client_hello() + .alpn() + .into_iter() + .flatten() + .map(|x| x.to_vec()) + .collect(); + let mut target_stream = TlsConnector::from(Arc::new(client_cfg)) + .connect_with( + ServerName::IpAddress(target.addr.ip().into()), + tcp_stream, + |conn| { + cfg.alpn_protocols + .extend(conn.alpn_protocol().into_iter().map(|p| p.to_vec())) + }, + ) + .await + .with_kind(crate::ErrorKind::OpenSsl)?; + let mut accept = mid.into_stream(Arc::new(cfg)); + let io = accept.get_mut().unwrap(); + let buffered = io.stop_buffering(); + io.write_all(&buffered).await?; + let mut tls_stream = match accept.await { + Ok(a) => a, Err(e) => { tracing::trace!( - "VHostController: failed to accept connection on port {port}: {e}" + "VHostController: failed to accept TLS connection on {bind}: {e}" ); tracing::trace!("{e:?}"); + return Ok(()); } + }; + tokio::io::copy_bidirectional(&mut tls_stream, &mut target_stream).await + } + Err(AlpnInfo::Reflect) => { + for proto in mid.client_hello().alpn().into_iter().flatten() { + cfg.alpn_protocols.push(proto.into()); + } + let mut accept = mid.into_stream(Arc::new(cfg)); + let io = accept.get_mut().unwrap(); + let buffered = io.stop_buffering(); + io.write_all(&buffered).await?; + let mut tls_stream = match accept.await { + Ok(a) => a, + Err(e) => { + tracing::trace!( + "VHostController: failed to accept TLS connection on {bind}: {e}" + ); + tracing::trace!("{e:?}"); + return Ok(()); + } + }; + tokio::io::copy_bidirectional(&mut tls_stream, &mut tcp_stream).await + } + Err(AlpnInfo::Specified(alpn)) => { + cfg.alpn_protocols = alpn.into_iter().map(|a| a.0).collect(); + let mut accept = mid.into_stream(Arc::new(cfg)); + let io = accept.get_mut().unwrap(); + let buffered = io.stop_buffering(); + io.write_all(&buffered).await?; + let mut tls_stream = match accept.await { + Ok(a) => a, + Err(e) => { + tracing::trace!( + "VHostController: failed to accept TLS connection on {bind}: {e}" + ); + tracing::trace!("{e:?}"); + return Ok(()); + } + }; + tokio::io::copy_bidirectional(&mut tls_stream, &mut tcp_stream).await + } + } + .map_or_else( + |e| { + use std::io::ErrorKind as E; + match e.kind() { + E::UnexpectedEof + | E::BrokenPipe + | E::ConnectionAborted + | E::ConnectionReset + | E::ConnectionRefused + | E::TimedOut + | E::Interrupted + | E::NotConnected => Ok(()), + _ => Err(e), + } + }, + |_| Ok(()), + )?; + } else { + // 503 + } + Ok::<_, Error>(()) + } + + #[instrument(skip_all)] + fn new( + port: u16, + db: TypedPatchDb, + iface_ctrl: Arc, + crypto_provider: Arc, + acme_tls_alpn_cache: AcmeTlsAlpnCache, + ) -> Result { + let mut listener = iface_ctrl.bind(port).with_kind(crate::ErrorKind::Network)?; + let (map_send, map_recv) = watch::channel(BTreeMap::new()); + Ok(Self { + mapping: map_send, + _thread: tokio::spawn(async move { + loop { + if let Err(e) = Self::accept( + &mut listener, + map_recv.clone(), + db.clone(), + acme_tls_alpn_cache.clone(), + crypto_provider.clone(), + ) + .await + { + tracing::error!( + "VHostController: failed to accept connection on {port}: {e}" + ); + tracing::debug!("{e:?}"); } } }) .into(), }) } - async fn add( - &self, - hostname: Option, - target: TargetInfo, - ) -> Result, Error> { - if let Some(mapping) = Weak::upgrade(&self.mapping) { - let mut writable = mapping.write().await; + fn add(&self, hostname: Option, target: TargetInfo) -> Result, Error> { + let mut res = Ok(Arc::new(())); + self.mapping.send_if_modified(|writable| { + let mut changed = false; let mut targets = writable.remove(&hostname).unwrap_or_default(); let rc = if let Some(rc) = Weak::upgrade(&targets.remove(&target).unwrap_or_default()) { rc } else { + changed = true; Arc::new(()) }; targets.insert(target, Arc::downgrade(&rc)); writable.insert(hostname, targets); - Ok(rc) + res = Ok(rc); + changed + }); + if !self.mapping.is_closed() { + res } else { Err(Error::new( eyre!("VHost Service Thread has exited"), @@ -542,33 +719,22 @@ impl VHostServer { )) } } - async fn gc(&self, hostname: Option) -> Result<(), Error> { - if let Some(mapping) = Weak::upgrade(&self.mapping) { - let mut writable = mapping.write().await; + fn gc(&self, hostname: Option) { + self.mapping.send_if_modified(|writable| { let mut targets = writable.remove(&hostname).unwrap_or_default(); + let pre = targets.len(); targets = targets .into_iter() .filter(|(_, rc)| rc.strong_count() > 0) .collect(); + let post = targets.len(); if !targets.is_empty() { writable.insert(hostname, targets); } - Ok(()) - } else { - Err(Error::new( - eyre!("VHost Service Thread has exited"), - crate::ErrorKind::Network, - )) - } + pre == post + }); } - async fn is_empty(&self) -> Result { - if let Some(mapping) = Weak::upgrade(&self.mapping) { - Ok(mapping.read().await.is_empty()) - } else { - Err(Error::new( - eyre!("VHost Service Thread has exited"), - crate::ErrorKind::Network, - )) - } + fn is_empty(&self) -> bool { + self.mapping.borrow().is_empty() } } diff --git a/core/startos/src/net/web_server.rs b/core/startos/src/net/web_server.rs index d1ad64d01..641a6f08d 100644 --- a/core/startos/src/net/web_server.rs +++ b/core/startos/src/net/web_server.rs @@ -1,134 +1,278 @@ -use std::convert::Infallible; +use std::future::Future; use std::net::SocketAddr; +use std::ops::Deref; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, RwLock}; use std::task::Poll; use std::time::Duration; -use axum::extract::Request; use axum::Router; -use axum_server::Handle; -use bytes::Bytes; -use futures::future::{ready, BoxFuture}; +use futures::future::Either; use futures::FutureExt; use helpers::NonDetachingJoinHandle; -use tokio::sync::{oneshot, watch}; +use hyper_util::rt::{TokioIo, TokioTimer}; +use tokio::net::{TcpListener, TcpStream}; +use tokio::sync::oneshot; use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext}; +use crate::net::network_interface::{ + NetworkInterfaceListener, SelfContainedNetworkInterfaceListener, +}; use crate::net::static_server::{ - diagnostic_ui_router, init_ui_router, install_ui_router, main_ui_router, refresher, + diagnostic_ui_router, init_ui_router, install_ui_router, main_ui_router, redirecter, refresher, setup_ui_router, }; use crate::prelude::*; +use crate::util::actor::background::BackgroundJobQueue; +use crate::util::sync::Watch; -#[derive(Clone)] -pub struct SwappableRouter(watch::Sender); -impl SwappableRouter { - pub fn new(router: Router) -> Self { - Self(watch::channel(router).0) - } - pub fn swap(&self, router: Router) { - let _ = self.0.send_replace(router); - } +pub struct Accepted { + pub https_redirect: bool, + pub stream: TcpStream, } -pub struct SwappableRouterService { - router: watch::Receiver, - changed: Option>, +pub trait Accept { + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll>; } -impl SwappableRouterService { - fn router(&self) -> Router { - self.router.borrow().clone() - } - fn changed(&mut self, cx: &mut std::task::Context<'_>) -> Poll<()> { - let mut changed = if let Some(changed) = self.changed.take() { - changed - } else { - let mut router = self.router.clone(); - async move { - router.changed().await; + +impl Accept for Vec { + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + for listener in &*self { + if let Poll::Ready((stream, _)) = listener.poll_accept(cx)? { + return Poll::Ready(Ok(Accepted { + https_redirect: false, + stream, + })); } - .boxed() - }; - if changed.poll_unpin(cx).is_ready() { - return Poll::Ready(()); } - self.changed = Some(changed); Poll::Pending } } -impl Clone for SwappableRouterService { - fn clone(&self) -> Self { +impl Accept for NetworkInterfaceListener { + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + NetworkInterfaceListener::poll_accept(self, cx, true).map(|res| { + res.map(|a| Accepted { + https_redirect: a.is_public, + stream: a.stream, + }) + }) + } +} + +impl Accept for Either { + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + match self { + Either::Left(a) => a.poll_accept(cx), + Either::Right(b) => b.poll_accept(cx), + } + } +} +impl Accept for Option { + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + match self { + None => Poll::Pending, + Some(a) => a.poll_accept(cx), + } + } +} + +#[pin_project::pin_project] +pub struct Acceptor { + acceptor: Watch, +} +impl Acceptor { + pub fn new(acceptor: A) -> Self { Self { - router: self.router.clone(), - changed: None, + acceptor: Watch::new(acceptor), } } + + fn poll_changed(&mut self, cx: &mut std::task::Context<'_>) -> Poll<()> { + self.acceptor.poll_changed(cx) + } + + fn poll_accept(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { + let _ = self.poll_changed(cx); + self.acceptor.peek_mut(|a| a.poll_accept(cx)) + } + + async fn accept(&mut self) -> Result { + std::future::poll_fn(|cx| self.poll_accept(cx)).await + } } -impl tower_service::Service> for SwappableRouterService -where - B: axum::body::HttpBody + Send + 'static, - B::Error: Into, -{ - type Response = >>::Response; - type Error = >>::Error; - type Future = >>::Future; - #[inline] - fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll> { - if self.changed(cx).is_ready() { - return Poll::Ready(Ok(())); - } - tower_service::Service::>::poll_ready(&mut self.router(), cx) - } - fn call(&mut self, req: Request) -> Self::Future { - self.router().call(req) +impl Acceptor> { + pub async fn bind(listen: impl IntoIterator) -> Result { + Ok(Self::new( + futures::future::try_join_all(listen.into_iter().map(TcpListener::bind)).await?, + )) } } -impl tower_service::Service for SwappableRouter { - type Response = SwappableRouterService; - type Error = Infallible; - type Future = futures::future::Ready>; - #[inline] - fn poll_ready( - &mut self, - _: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, _: T) -> Self::Future { - ready(Ok(SwappableRouterService { - router: self.0.subscribe(), - changed: None, - })) +pub type UpgradableListener = + Option>; + +impl Acceptor { + pub fn bind_upgradable(listener: SelfContainedNetworkInterfaceListener) -> Self { + Self::new(Some(Either::Left(listener))) } } -pub struct WebServer { +pub struct WebServerAcceptorSetter { + acceptor: Watch, +} +impl WebServerAcceptorSetter>> { + pub fn try_upgrade Result>(&self, f: F) -> Result<(), Error> { + let mut res = Ok(()); + self.acceptor.send_modify(|a| { + *a = match a.take() { + Some(Either::Left(a)) => match f(a) { + Ok(b) => Some(Either::Right(b)), + Err(e) => { + res = Err(e); + None + } + }, + x => x, + } + }); + res + } +} +impl Deref for WebServerAcceptorSetter { + type Target = Watch; + fn deref(&self) -> &Self::Target { + &self.acceptor + } +} + +pub struct WebServer { shutdown: oneshot::Sender<()>, - router: SwappableRouter, + router: Watch>, + acceptor: Watch, thread: NonDetachingJoinHandle<()>, } -impl WebServer { - pub fn new(bind: SocketAddr) -> Self { - let router = SwappableRouter::new(refresher()); - let thread_router = router.clone(); +impl WebServer { + pub fn acceptor_setter(&self) -> WebServerAcceptorSetter { + WebServerAcceptorSetter { + acceptor: self.acceptor.clone(), + } + } + + pub fn new(mut acceptor: Acceptor) -> Self { + let acceptor_send = acceptor.acceptor.clone(); + let router = Watch::>::new(None); + let service = router.clone_unseen(); let (shutdown, shutdown_recv) = oneshot::channel(); let thread = NonDetachingJoinHandle::from(tokio::spawn(async move { - let handle = Handle::new(); - let mut server = axum_server::bind(bind).handle(handle.clone()); - server.http_builder().http1().preserve_header_case(true); - server.http_builder().http1().title_case_headers(true); + #[derive(Clone)] + struct QueueRunner { + queue: Arc>>, + } + impl hyper::rt::Executor for QueueRunner + where + Fut: Future + Send + 'static, + { + fn execute(&self, fut: Fut) { + if let Some(q) = &*self.queue.read().unwrap() { + q.add_job(fut); + } else { + tracing::warn!("job queued after shutdown"); + } + } + } - if let (Err(e), _) = tokio::join!(server.serve(thread_router), async { - let _ = shutdown_recv.await; - handle.graceful_shutdown(Some(Duration::from_secs(0))); - }) { - tracing::error!("Spawning hyper server error: {}", e); + struct SwappableRouter(Watch>, bool); + impl hyper::service::Service> for SwappableRouter { + type Response = , + >>::Response; + type Error = , + >>::Error; + type Future = , + >>::Future; + + fn call(&self, req: hyper::Request) -> Self::Future { + use tower_service::Service; + + if self.1 { + redirecter().call(req) + } else { + let router = self.0.read(); + if let Some(mut router) = router { + router.call(req) + } else { + refresher().call(req) + } + } + } + } + + let accept = AtomicBool::new(true); + let queue_cell = Arc::new(RwLock::new(None)); + let graceful = hyper_util::server::graceful::GracefulShutdown::new(); + let mut server = hyper_util::server::conn::auto::Builder::new(QueueRunner { + queue: queue_cell.clone(), + }); + server + .http1() + .timer(TokioTimer::new()) + .title_case_headers(true) + .preserve_header_case(true) + .http2() + .timer(TokioTimer::new()) + .enable_connect_protocol() + .keep_alive_interval(Duration::from_secs(60)) + .keep_alive_timeout(Duration::from_secs(300)); + let (queue, mut runner) = BackgroundJobQueue::new(); + *queue_cell.write().unwrap() = Some(queue.clone()); + + let handler = async { + loop { + if let Err(e) = async { + let accepted = acceptor.accept().await?; + queue.add_job( + graceful.watch( + server + .serve_connection_with_upgrades( + TokioIo::new(accepted.stream), + SwappableRouter(service.clone(), accepted.https_redirect), + ) + .into_owned(), + ), + ); + + Ok::<_, Error>(()) + } + .await + { + tracing::error!("Error accepting HTTP connection: {e}"); + tracing::debug!("{e:?}"); + } + } + } + .boxed(); + + tokio::select! { + _ = shutdown_recv => (), + _ = handler => (), + _ = &mut runner => (), + } + + accept.store(false, std::sync::atomic::Ordering::SeqCst); + drop(queue); + drop(queue_cell.write().unwrap().take()); + + if !runner.is_empty() { + runner.await; } })); Self { shutdown, router, thread, + acceptor: acceptor_send, } } @@ -138,7 +282,7 @@ impl WebServer { } pub fn serve_router(&mut self, router: Router) { - self.router.swap(router) + self.router.send(Some(router)) } pub fn serve_main(&mut self, ctx: RpcContext) { diff --git a/core/startos/src/net/wifi.rs b/core/startos/src/net/wifi.rs index 056a403de..9b858aafe 100644 --- a/core/startos/src/net/wifi.rs +++ b/core/startos/src/net/wifi.rs @@ -298,7 +298,7 @@ fn display_wifi_info(params: WithIoFormat, info: WifiListInfo) { let mut table_global = Table::new(); table_global.add_row(row![bc => "CONNECTED", - "SIGNAL_STRENGTH", + "SIGNAL STRENGTH", "COUNTRY", "ETHERNET", ]); @@ -306,12 +306,12 @@ fn display_wifi_info(params: WithIoFormat, info: WifiListInfo) { &info .connected .as_ref() - .map_or("[N/A]".to_owned(), |c| c.0.clone()), + .map_or("N/A".to_owned(), |c| c.0.clone()), &info .connected .as_ref() .and_then(|x| info.ssids.get(x)) - .map_or("[N/A]".to_owned(), |ss| format!("{}", ss.0)), + .map_or("N/A".to_owned(), |ss| format!("{}", ss.0)), info.country.as_ref().map(|c| c.alpha2()).unwrap_or("00"), &format!("{}", info.ethernet) ]); @@ -897,32 +897,28 @@ impl TypedValueParser for CountryCodeParser { } #[instrument(skip_all)] -pub async fn synchronize_wpa_supplicant_conf>( +pub async fn synchronize_network_manager>( main_datadir: P, - wifi: &mut WifiInfo, + wifi: &WifiInfo, ) -> Result<(), Error> { - wifi.interface = find_wifi_iface().await?; - let Some(wifi_iface) = &wifi.interface else { - return Ok(()); - }; let persistent = main_datadir.as_ref().join("system-connections"); - tracing::debug!("persistent: {:?}", persistent); - // let supplicant = Path::new("/etc/wpa_supplicant.conf"); if tokio::fs::metadata(&persistent).await.is_err() { tokio::fs::create_dir_all(&persistent).await?; } crate::disk::mount::util::bind(&persistent, "/etc/NetworkManager/system-connections", false) .await?; - // if tokio::fs::metadata(&supplicant).await.is_err() { - // tokio::fs::write(&supplicant, include_str!("wpa_supplicant.conf.base")).await?; - // } Command::new("systemctl") .arg("restart") .arg("NetworkManager") .invoke(ErrorKind::Wifi) .await?; + + let Some(wifi_iface) = &wifi.interface else { + return Ok(()); + }; + Command::new("ifconfig") .arg(wifi_iface) .arg("up") diff --git a/core/startos/src/notifications.rs b/core/startos/src/notifications.rs index 4b45531a4..19376026d 100644 --- a/core/startos/src/notifications.rs +++ b/core/startos/src/notifications.rs @@ -13,11 +13,11 @@ use serde::{Deserialize, Serialize}; use tracing::instrument; use ts_rs::TS; +use crate::backup::BackupReport; use crate::context::{CliContext, RpcContext}; -use crate::db::model::DatabaseModel; +use crate::db::model::{Database, DatabaseModel}; use crate::prelude::*; use crate::util::serde::HandlerExtSerde; -use crate::{backup::BackupReport, db::model::Database}; // #[command(subcommands(list, delete, delete_before, create))] pub fn notification() -> ParentHandler { diff --git a/core/startos/src/os_install/gpt.rs b/core/startos/src/os_install/gpt.rs index 01703083b..4833f4ea7 100644 --- a/core/startos/src/os_install/gpt.rs +++ b/core/startos/src/os_install/gpt.rs @@ -50,7 +50,7 @@ pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result {{ + tracing::debug!("[{}:{}:{}]", file!(), line!(), column!()); + }}; + ($e:expr) => {{ + let e = $e; + tracing::debug!("[{}:{}:{}] {} = {e:?}", file!(), line!(), column!(), stringify!($e)); + e + }}; + ($($e:expr),+) => { + ($( + crate::dbg!($e) + ),+) + } +} diff --git a/core/startos/src/registry/context.rs b/core/startos/src/registry/context.rs index 16c6465ed..d78fde50e 100644 --- a/core/startos/src/registry/context.rs +++ b/core/startos/src/registry/context.rs @@ -19,7 +19,6 @@ use crate::context::config::{ContextConfig, CONFIG_PATH}; use crate::context::{CliContext, RpcContext}; use crate::prelude::*; use crate::registry::auth::{SignatureHeader, AUTH_SIG_HEADER}; -use crate::registry::device_info::{DeviceInfo, DEVICE_INFO_HEADER}; use crate::registry::signer::sign::AnySigningKey; use crate::registry::RegistryDatabase; use crate::rpc_continuations::RpcContinuations; diff --git a/core/startos/src/registry/mod.rs b/core/startos/src/registry/mod.rs index 0cbbce4e0..4e1411ea9 100644 --- a/core/startos/src/registry/mod.rs +++ b/core/startos/src/registry/mod.rs @@ -2,7 +2,6 @@ use std::collections::{BTreeMap, BTreeSet}; use axum::Router; use futures::future::ready; -use imbl_value::InternedString; use models::DataUrl; use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler, Server}; use serde::{Deserialize, Serialize}; @@ -11,13 +10,13 @@ use ts_rs::TS; use crate::context::CliContext; use crate::middleware::cors::Cors; use crate::net::static_server::{bad_request, not_found, server_error}; -use crate::net::web_server::WebServer; +use crate::net::web_server::{Accept, WebServer}; use crate::prelude::*; use crate::registry::auth::Auth; use crate::registry::context::RegistryContext; use crate::registry::device_info::DeviceInfoMiddleware; use crate::registry::os::index::OsIndex; -use crate::registry::package::index::{Category, PackageIndex}; +use crate::registry::package::index::PackageIndex; use crate::registry::signer::SignerInfo; use crate::rpc_continuations::Guid; use crate::util::serde::HandlerExtSerde; @@ -144,7 +143,7 @@ pub fn registry_router(ctx: RegistryContext) -> Router { ) } -impl WebServer { +impl WebServer { pub fn serve_registry(&mut self, ctx: RegistryContext) { self.serve_router(registry_router(ctx)) } diff --git a/core/startos/src/registry/package/index.rs b/core/startos/src/registry/package/index.rs index 428200165..9973bae7e 100644 --- a/core/startos/src/registry/package/index.rs +++ b/core/startos/src/registry/package/index.rs @@ -72,7 +72,6 @@ pub struct PackageVersionInfo { pub icon: DataUrl<'static>, pub description: Description, pub release_notes: String, - #[ts(type = "string")] pub git_hash: GitHash, #[ts(type = "string")] pub license: InternedString, diff --git a/core/startos/src/registry/signer/commitment/merkle_archive.rs b/core/startos/src/registry/signer/commitment/merkle_archive.rs index 1b9d7d1e0..b27fb7ef4 100644 --- a/core/startos/src/registry/signer/commitment/merkle_archive.rs +++ b/core/startos/src/registry/signer/commitment/merkle_archive.rs @@ -24,10 +24,10 @@ impl MerkleArchiveCommitment { pub fn from_query(query: &str) -> Result, Error> { let mut root_sighash = None; let mut root_maxsize = None; - for (k, v) in form_urlencoded::parse(dbg!(query).as_bytes()) { + for (k, v) in form_urlencoded::parse(query.as_bytes()) { match &*k { "rootSighash" => { - root_sighash = Some(dbg!(v).parse()?); + root_sighash = Some(v.parse()?); } "rootMaxsize" => { root_maxsize = Some(v.parse()?); diff --git a/core/startos/src/s9pk/git_hash.rs b/core/startos/src/s9pk/git_hash.rs index 02f83bf4a..762ef8704 100644 --- a/core/startos/src/s9pk/git_hash.rs +++ b/core/startos/src/s9pk/git_hash.rs @@ -1,11 +1,13 @@ use std::path::Path; use tokio::process::Command; +use ts_rs::TS; use crate::prelude::*; use crate::util::Invoke; -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, TS)] +#[ts(type = "string")] pub struct GitHash(String); impl GitHash { @@ -31,6 +33,31 @@ impl GitHash { } Ok(GitHash(hash)) } + pub fn load_sync() -> Option { + let mut hash = String::from_utf8( + std::process::Command::new("git") + .arg("rev-parse") + .arg("HEAD") + .output() + .ok()? + .stdout, + ) + .ok()?; + if !std::process::Command::new("git") + .arg("diff-index") + .arg("--quiet") + .arg("HEAD") + .arg("--") + .output() + .ok()? + .status + .success() + { + hash += "-modified"; + } + + Some(GitHash(hash)) + } } impl AsRef for GitHash { diff --git a/core/startos/src/s9pk/v2/manifest.rs b/core/startos/src/s9pk/v2/manifest.rs index 85f3cd796..11ea0d9af 100644 --- a/core/startos/src/s9pk/v2/manifest.rs +++ b/core/startos/src/s9pk/v2/manifest.rs @@ -3,7 +3,6 @@ use std::path::Path; use color_eyre::eyre::eyre; use exver::{Version, VersionRange}; -use helpers::const_true; use imbl_value::InternedString; pub use models::PackageId; use models::{mime, ImageId, VolumeId}; @@ -62,8 +61,8 @@ pub struct Manifest { pub dependencies: Dependencies, #[serde(default)] pub hardware_requirements: HardwareRequirements, - #[serde(default)] - #[ts(type = "string | null")] + #[ts(optional)] + #[serde(default = "GitHash::load_sync")] pub git_hash: Option, #[serde(default = "current_version")] #[ts(type = "string")] diff --git a/core/startos/src/service/effects/callbacks.rs b/core/startos/src/service/effects/callbacks.rs index 65eb707d8..19946672c 100644 --- a/core/startos/src/service/effects/callbacks.rs +++ b/core/startos/src/service/effects/callbacks.rs @@ -294,7 +294,7 @@ impl CallbackHandler { } } pub async fn call(mut self, args: Vector) -> Result<(), Error> { - dbg!(eyre!("callback fired: {}", self.handle.is_active())); + crate::dbg!(eyre!("callback fired: {}", self.handle.is_active())); if let Some(seed) = self.seed.upgrade() { seed.persistent_container .callback(self.handle.take(), args) diff --git a/core/startos/src/service/effects/dependency.rs b/core/startos/src/service/effects/dependency.rs index ed43b7a9c..2a16b4155 100644 --- a/core/startos/src/service/effects/dependency.rs +++ b/core/startos/src/service/effects/dependency.rs @@ -17,11 +17,11 @@ use crate::db::model::package::{ use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::idmapped::IdMapped; use crate::disk::mount::filesystem::{FileSystem, MountType}; -use crate::rpc_continuations::Guid; use crate::service::effects::prelude::*; use crate::status::health_check::NamedHealthCheckResult; use crate::util::Invoke; use crate::volume::data_dir; +use crate::DATA_DIR; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[ts(export)] @@ -55,7 +55,7 @@ pub async fn mount( let context = context.deref()?; let subpath = subpath.unwrap_or_default(); let subpath = subpath.strip_prefix("/").unwrap_or(&subpath); - let source = data_dir(&context.seed.ctx.datadir, &package_id, &volume_id).join(subpath); + let source = data_dir(DATA_DIR, &package_id, &volume_id).join(subpath); if tokio::fs::metadata(&source).await.is_err() { tokio::fs::create_dir_all(&source).await?; } diff --git a/core/startos/src/service/effects/mod.rs b/core/startos/src/service/effects/mod.rs index f68985268..e9df6f9f2 100644 --- a/core/startos/src/service/effects/mod.rs +++ b/core/startos/src/service/effects/mod.rs @@ -130,10 +130,6 @@ pub fn handler() -> ParentHandler { "get-host-info", from_fn_async(net::host::get_host_info).no_cli(), ) - .subcommand( - "get-primary-url", - from_fn_async(net::host::get_primary_url).no_cli(), - ) .subcommand( "get-container-ip", from_fn_async(net::info::get_container_ip).no_cli(), diff --git a/core/startos/src/service/effects/net/bind.rs b/core/startos/src/service/effects/net/bind.rs index 5619375eb..2db3eb2ca 100644 --- a/core/startos/src/service/effects/net/bind.rs +++ b/core/startos/src/service/effects/net/bind.rs @@ -1,14 +1,12 @@ use models::{HostId, PackageId}; -use crate::net::host::binding::{BindId, BindOptions, LanInfo}; -use crate::net::host::HostKind; +use crate::net::host::binding::{BindId, BindOptions, NetInfo}; use crate::service::effects::prelude::*; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] pub struct BindParams { - kind: HostKind, id: HostId, internal_port: u16, #[serde(flatten)] @@ -17,15 +15,18 @@ pub struct BindParams { pub async fn bind( context: EffectContext, BindParams { - kind, id, internal_port, options, }: BindParams, ) -> Result<(), Error> { let context = context.deref()?; - let mut svc = context.seed.persistent_container.net_service.lock().await; - svc.bind(kind, id, internal_port, options).await + context + .seed + .persistent_container + .net_service + .bind(id, internal_port, options) + .await } #[derive(Debug, Clone, Serialize, Deserialize, TS, Parser)] @@ -41,8 +42,12 @@ pub async fn clear_bindings( ClearBindingsParams { except }: ClearBindingsParams, ) -> Result<(), Error> { let context = context.deref()?; - let mut svc = context.seed.persistent_container.net_service.lock().await; - svc.clear_bindings(except.into_iter().collect()).await?; + context + .seed + .persistent_container + .net_service + .clear_bindings(except.into_iter().collect()) + .await?; Ok(()) } @@ -53,15 +58,36 @@ pub struct GetServicePortForwardParams { #[ts(optional)] package_id: Option, host_id: HostId, - internal_port: u32, + internal_port: u16, } pub async fn get_service_port_forward( context: EffectContext, - data: GetServicePortForwardParams, -) -> Result { - let internal_port = data.internal_port as u16; - + GetServicePortForwardParams { + package_id, + host_id, + internal_port, + }: GetServicePortForwardParams, +) -> Result { let context = context.deref()?; - let net_service = context.seed.persistent_container.net_service.lock().await; - net_service.get_lan_port(data.host_id, internal_port) + + let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); + + Ok(context + .seed + .ctx + .db + .peek() + .await + .as_public() + .as_package_data() + .as_idx(&package_id) + .or_not_found(&package_id)? + .as_hosts() + .as_idx(&host_id) + .or_not_found(&host_id)? + .as_bindings() + .de()? + .get(&internal_port) + .or_not_found(lazy_format!("binding for port {internal_port}"))? + .net) } diff --git a/core/startos/src/service/effects/net/host.rs b/core/startos/src/service/effects/net/host.rs index d320e7fe9..570d5033d 100644 --- a/core/startos/src/service/effects/net/host.rs +++ b/core/startos/src/service/effects/net/host.rs @@ -1,35 +1,10 @@ use models::{HostId, PackageId}; -use crate::net::host::address::HostAddress; use crate::net::host::Host; use crate::service::effects::callbacks::CallbackHandler; use crate::service::effects::prelude::*; use crate::service::rpc::CallbackId; -#[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts(export)] -#[serde(rename_all = "camelCase")] -pub struct GetPrimaryUrlParams { - #[ts(optional)] - package_id: Option, - host_id: HostId, - #[ts(optional)] - callback: Option, -} -pub async fn get_primary_url( - context: EffectContext, - GetPrimaryUrlParams { - package_id, - host_id, - callback, - }: GetPrimaryUrlParams, -) -> Result, Error> { - let context = context.deref()?; - let package_id = package_id.unwrap_or_else(|| context.seed.id.clone()); - - Ok(None) // TODO -} - #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] #[ts(export)] diff --git a/core/startos/src/service/effects/net/info.rs b/core/startos/src/service/effects/net/info.rs index c33a1a81e..fe6623f44 100644 --- a/core/startos/src/service/effects/net/info.rs +++ b/core/startos/src/service/effects/net/info.rs @@ -4,6 +4,5 @@ use crate::service::effects::prelude::*; pub async fn get_container_ip(context: EffectContext) -> Result { let context = context.deref()?; - let net_service = context.seed.persistent_container.net_service.lock().await; - Ok(net_service.get_ip()) + Ok(context.seed.persistent_container.net_service.get_ip().await) } diff --git a/core/startos/src/service/effects/net/interface.rs b/core/startos/src/service/effects/net/interface.rs index 44258c36a..5de9638c4 100644 --- a/core/startos/src/service/effects/net/interface.rs +++ b/core/startos/src/service/effects/net/interface.rs @@ -15,7 +15,6 @@ pub struct ExportServiceInterfaceParams { id: ServiceInterfaceId, name: String, description: String, - has_primary: bool, masked: bool, address_info: AddressInfo, r#type: ServiceInterfaceType, @@ -26,7 +25,6 @@ pub async fn export_service_interface( id, name, description, - has_primary, masked, address_info, r#type, @@ -39,7 +37,6 @@ pub async fn export_service_interface( id: id.clone(), name, description, - has_primary, masked, address_info, interface_type: r#type, diff --git a/core/startos/src/service/effects/net/ssl.rs b/core/startos/src/service/effects/net/ssl.rs index d37a2d241..66b4fa1e6 100644 --- a/core/startos/src/service/effects/net/ssl.rs +++ b/core/startos/src/service/effects/net/ssl.rs @@ -51,10 +51,16 @@ pub async fn get_ssl_certificate( .iter() .map(|(_, m)| m.as_hosts().as_entries()) .flatten_ok() - .map_ok(|(_, m)| m.as_addresses().de()) + .map_ok(|(_, m)| { + Ok(m.as_onions() + .de()? + .iter() + .map(InternedString::from_display) + .chain(m.as_domains().keys()?) + .collect::>()) + }) .map(|a| a.and_then(|a| a)) .flatten_ok() - .map_ok(|a| InternedString::from_display(&a)) .try_collect::<_, BTreeSet<_>, _>()?; for hostname in &hostnames { if let Some(internal) = hostname @@ -135,10 +141,16 @@ pub async fn get_ssl_key( .into_iter() .map(|m| m.as_hosts().as_entries()) .flatten_ok() - .map_ok(|(_, m)| m.as_addresses().de()) + .map_ok(|(_, m)| { + Ok(m.as_onions() + .de()? + .iter() + .map(InternedString::from_display) + .chain(m.as_domains().keys()?) + .collect::>()) + }) .map(|a| a.and_then(|a| a)) .flatten_ok() - .map_ok(|a| InternedString::from_display(&a)) .try_collect::<_, BTreeSet<_>, _>()?; for hostname in &hostnames { if let Some(internal) = hostname diff --git a/core/startos/src/service/effects/store.rs b/core/startos/src/service/effects/store.rs index 1d4a07086..39166c333 100644 --- a/core/startos/src/service/effects/store.rs +++ b/core/startos/src/service/effects/store.rs @@ -26,7 +26,7 @@ pub async fn get_store( callback, }: GetStoreParams, ) -> Result { - dbg!(&callback); + crate::dbg!(&callback); let context = context.deref()?; let peeked = context.seed.ctx.db.peek().await; let package_id = package_id.unwrap_or(context.seed.id.clone()); diff --git a/core/startos/src/service/effects/subcontainer/mod.rs b/core/startos/src/service/effects/subcontainer/mod.rs index 65fcbd387..943c70dbf 100644 --- a/core/startos/src/service/effects/subcontainer/mod.rs +++ b/core/startos/src/service/effects/subcontainer/mod.rs @@ -4,12 +4,11 @@ use imbl_value::InternedString; use models::ImageId; use tokio::process::Command; +use crate::disk::mount::filesystem::overlayfs::OverlayGuard; use crate::rpc_continuations::Guid; use crate::service::effects::prelude::*; +use crate::service::persistent_container::Subcontainer; use crate::util::Invoke; -use crate::{ - disk::mount::filesystem::overlayfs::OverlayGuard, service::persistent_container::Subcontainer, -}; #[cfg(feature = "container-runtime")] mod sync; diff --git a/core/startos/src/service/mod.rs b/core/startos/src/service/mod.rs index d73c51beb..2a18ea518 100644 --- a/core/startos/src/service/mod.rs +++ b/core/startos/src/service/mod.rs @@ -48,7 +48,7 @@ use crate::util::net::WebSocketExt; use crate::util::serde::{NoOutput, Pem}; use crate::util::Never; use crate::volume::data_dir; -use crate::CAP_1_KiB; +use crate::{CAP_1_KiB, DATA_DIR, PACKAGE_DATA}; pub mod action; pub mod cli; @@ -149,10 +149,10 @@ impl ServiceRef { .values() .flat_map(|h| h.bindings.values()) .flat_map(|b| { - b.lan + b.net .assigned_port .into_iter() - .chain(b.lan.assigned_ssl_port) + .chain(b.net.assigned_ssl_port) }), ); Ok(()) @@ -167,17 +167,18 @@ impl ServiceRef { { let state = pde.state_info.expect_removing()?; for volume_id in &state.manifest.volumes { - let path = data_dir(&ctx.datadir, &state.manifest.id, volume_id); + let path = data_dir(DATA_DIR, &state.manifest.id, volume_id); if tokio::fs::metadata(&path).await.is_ok() { tokio::fs::remove_dir_all(&path).await?; } } - let logs_dir = ctx.datadir.join("logs").join(&state.manifest.id); + let logs_dir = Path::new(PACKAGE_DATA) + .join("logs") + .join(&state.manifest.id); if tokio::fs::metadata(&logs_dir).await.is_ok() { tokio::fs::remove_dir_all(&logs_dir).await?; } - let archive_path = ctx - .datadir + let archive_path = Path::new(PACKAGE_DATA) .join("archive") .join("installed") .join(&state.manifest.id); @@ -278,7 +279,7 @@ impl Service { let ctx = ctx.clone(); move |s9pk: S9pk, i: Model| async move { for volume_id in &s9pk.as_manifest().volumes { - let path = data_dir(&ctx.datadir, &s9pk.as_manifest().id, volume_id); + let path = data_dir(DATA_DIR, &s9pk.as_manifest().id, volume_id); if tokio::fs::metadata(&path).await.is_err() { tokio::fs::create_dir_all(&path).await?; } @@ -291,7 +292,7 @@ impl Service { Self::new(ctx, s9pk, start_stop).await.map(Some) } }; - let s9pk_dir = ctx.datadir.join(PKG_ARCHIVE_DIR).join("installed"); // TODO: make this based on hash + let s9pk_dir = Path::new(DATA_DIR).join(PKG_ARCHIVE_DIR).join("installed"); // TODO: make this based on hash let s9pk_path = s9pk_dir.join(id).with_extension("s9pk"); let Some(entry) = ctx .db @@ -604,27 +605,11 @@ impl Service { }) } - pub async fn update_host(&self, host_id: HostId) -> Result<(), Error> { - let host = self - .seed - .ctx - .db - .peek() - .await - .as_public() - .as_package_data() - .as_idx(&self.seed.id) - .or_not_found(&self.seed.id)? - .as_hosts() - .as_idx(&host_id) - .or_not_found(&host_id)? - .de()?; + pub async fn sync_host(&self, host_id: HostId) -> Result<(), Error> { self.seed .persistent_container .net_service - .lock() - .await - .update(host_id, host) + .sync_host(host_id) .await } } @@ -934,7 +919,6 @@ pub async fn attach( .with_kind(ErrorKind::Network)?; current_out = "stdout"; } - dbg!(¤t_out); ws.send(Message::Binary(out)) .await .with_kind(ErrorKind::Network)?; @@ -948,7 +932,6 @@ pub async fn attach( .with_kind(ErrorKind::Network)?; current_out = "stderr"; } - dbg!(¤t_out); ws.send(Message::Binary(err)) .await .with_kind(ErrorKind::Network)?; diff --git a/core/startos/src/service/persistent_container.rs b/core/startos/src/service/persistent_container.rs index 13cb7688c..910255e7d 100644 --- a/core/startos/src/service/persistent_container.rs +++ b/core/startos/src/service/persistent_container.rs @@ -39,7 +39,7 @@ use crate::util::io::create_file; use crate::util::rpc_client::UnixRpcClient; use crate::util::Invoke; use crate::volume::data_dir; -use crate::ARCH; +use crate::{ARCH, DATA_DIR, PACKAGE_DATA}; const RPC_CONNECT_TIMEOUT: Duration = Duration::from_secs(10); @@ -110,7 +110,7 @@ pub struct PersistentContainer { pub(super) images: BTreeMap>, pub(super) subcontainers: Arc>>, pub(super) state: Arc>, - pub(super) net_service: Mutex, + pub(super) net_service: NetService, destroyed: bool, } @@ -121,8 +121,8 @@ impl PersistentContainer { .lxc_manager .create( Some( - &ctx.datadir - .join("package-data/logs") + &Path::new(PACKAGE_DATA) + .join("logs") .join(&s9pk.as_manifest().id), ), LxcConfig::default(), @@ -157,7 +157,7 @@ impl PersistentContainer { .await?; let mount = MountGuard::mount( &IdMapped::new( - Bind::new(data_dir(&ctx.datadir, &s9pk.as_manifest().id, volume)), + Bind::new(data_dir(DATA_DIR, &s9pk.as_manifest().id, volume)), 0, 100000, 65536, @@ -285,7 +285,7 @@ impl PersistentContainer { images, subcontainers: Arc::new(Mutex::new(BTreeMap::new())), state: Arc::new(watch::channel(ServiceState::new(start)).0), - net_service: Mutex::new(net_service), + net_service, destroyed: false, }) } @@ -452,7 +452,7 @@ impl PersistentContainer { #[instrument(skip_all)] pub async fn exit(mut self) -> Result<(), Error> { if let Some(destroy) = self.destroy(false) { - dbg!(destroy.await)?; + destroy.await?; } tracing::info!("Service for {} exited", self.s9pk.as_manifest().id); diff --git a/core/startos/src/service/rpc.rs b/core/startos/src/service/rpc.rs index f008de5c7..61eb5d592 100644 --- a/core/startos/src/service/rpc.rs +++ b/core/startos/src/service/rpc.rs @@ -155,7 +155,7 @@ impl serde::Serialize for Sandbox { pub struct CallbackId(u64); impl CallbackId { pub fn register(self, container: &PersistentContainer) -> CallbackHandle { - dbg!(eyre!( + crate::dbg!(eyre!( "callback {} registered for {}", self.0, container.s9pk.as_manifest().id diff --git a/core/startos/src/service/service_actor.rs b/core/startos/src/service/service_actor.rs index 712baaf1e..a56c92288 100644 --- a/core/startos/src/service/service_actor.rs +++ b/core/startos/src/service/service_actor.rs @@ -36,7 +36,7 @@ impl Actor for ServiceActor { ServiceActorLoopNext::DontWait => (), } } - }) + }); } } @@ -92,7 +92,6 @@ async fn service_actor_loop( .. } => MainStatus::Stopped, }; - let previous = i.as_status().de()?; i.as_status_mut().ser(&main_status)?; return Ok(previous .major_changes(&main_status) diff --git a/core/startos/src/service/service_map.rs b/core/startos/src/service/service_map.rs index 0faab8dd5..de450706e 100644 --- a/core/startos/src/service/service_map.rs +++ b/core/startos/src/service/service_map.rs @@ -1,9 +1,11 @@ +use std::path::Path; use std::sync::Arc; use std::time::Duration; use color_eyre::eyre::eyre; use futures::future::BoxFuture; -use futures::{Future, FutureExt}; +use futures::stream::FuturesUnordered; +use futures::{Future, FutureExt, StreamExt}; use helpers::NonDetachingJoinHandle; use imbl::OrdMap; use imbl_value::InternedString; @@ -27,6 +29,7 @@ use crate::service::start_stop::StartStop; use crate::service::{LoadDisposition, Service, ServiceRef}; use crate::status::MainStatus; use crate::util::serde::Pem; +use crate::DATA_DIR; pub type DownloadInstallFuture = BoxFuture<'static, Result>; pub type InstallFuture = BoxFuture<'static, Result<(), Error>>; @@ -66,8 +69,12 @@ impl ServiceMap { progress.start(); let ids = ctx.db.peek().await.as_public().as_package_data().keys()?; progress.set_total(ids.len() as u64); - for id in ids { - if let Err(e) = self.load(ctx, &id, LoadDisposition::Retry).await { + let mut jobs = FuturesUnordered::new(); + for id in &ids { + jobs.push(self.load(ctx, id, LoadDisposition::Retry)); + } + while let Some(res) = jobs.next().await { + if let Err(e) = res { tracing::error!("Error loading installed package as service: {e}"); tracing::debug!("{e:?}"); } @@ -220,8 +227,7 @@ impl ServiceMap { Ok(async move { let (installed_path, sync_progress_task) = reload_guard .handle(async { - let download_path = ctx - .datadir + let download_path = Path::new(DATA_DIR) .join(PKG_ARCHIVE_DIR) .join("downloading") .join(&id) @@ -251,8 +257,7 @@ impl ServiceMap { file.sync_all().await?; download_progress.complete(); - let installed_path = ctx - .datadir + let installed_path = Path::new(DATA_DIR) .join(PKG_ARCHIVE_DIR) .join("installed") .join(&id) diff --git a/core/startos/src/service/transition/backup.rs b/core/startos/src/service/transition/backup.rs index 0d4116078..6205cdd61 100644 --- a/core/startos/src/service/transition/backup.rs +++ b/core/startos/src/service/transition/backup.rs @@ -15,6 +15,7 @@ use crate::service::ServiceActor; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::{ConflictBuilder, Handler}; use crate::util::future::RemoteCancellable; +use crate::util::serde::NoOutput; pub(in crate::service) struct Backup { pub path: PathBuf, @@ -48,7 +49,7 @@ impl Handler for ServiceActor { .mount_backup(path, ReadWrite) .await?; seed.persistent_container - .execute(id, ProcedureName::CreateBackup, Value::Null, None) + .execute::(id, ProcedureName::CreateBackup, Value::Null, None) .await?; backup_guard.unmount(true).await?; diff --git a/core/startos/src/service/transition/restore.rs b/core/startos/src/service/transition/restore.rs index 1c4020ea4..7061b0c1e 100644 --- a/core/startos/src/service/transition/restore.rs +++ b/core/startos/src/service/transition/restore.rs @@ -11,6 +11,7 @@ use crate::service::ServiceActor; use crate::util::actor::background::BackgroundJobQueue; use crate::util::actor::{ConflictBuilder, Handler}; use crate::util::future::RemoteCancellable; +use crate::util::serde::NoOutput; pub(in crate::service) struct Restore { pub path: PathBuf, @@ -38,7 +39,7 @@ impl Handler for ServiceActor { .mount_backup(path, ReadOnly) .await?; seed.persistent_container - .execute(id, ProcedureName::RestoreBackup, Value::Null, None) + .execute::(id, ProcedureName::RestoreBackup, Value::Null, None) .await?; backup_guard.unmount(true).await?; @@ -48,7 +49,7 @@ impl Handler for ServiceActor { Ok::<_, Error>(()) } .map(|x| { - if let Err(err) = dbg!(x) { + if let Err(err) = x { tracing::debug!("{:?}", err); tracing::warn!("{}", err); } diff --git a/core/startos/src/setup.rs b/core/startos/src/setup.rs index 1319ffae4..186fbb0c4 100644 --- a/core/startos/src/setup.rs +++ b/core/startos/src/setup.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use std::time::Duration; use color_eyre::eyre::eyre; +use const_format::formatcp; use josekit::jwk::Jwk; use patch_db::json_ptr::ROOT; use rpc_toolkit::yajrc::RpcError; @@ -30,7 +31,7 @@ use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard}; use crate::disk::util::{pvscan, recovery_info, DiskInfo, StartOsRecoveryInfo}; use crate::disk::REPAIR_DISK_PATH; use crate::init::{init, InitPhases, InitResult}; -use crate::net::net_controller::PreInitNetController; +use crate::net::net_controller::NetController; use crate::net::ssl::root_ca_start_time; use crate::prelude::*; use crate::progress::{FullProgress, PhaseProgressTrackerHandle}; @@ -38,7 +39,7 @@ use crate::rpc_continuations::Guid; use crate::util::crypto::EncryptedWire; use crate::util::io::{create_file, dir_copy, dir_size, Counter}; use crate::util::Invoke; -use crate::{Error, ErrorKind, ResultExt}; +use crate::{Error, ErrorKind, ResultExt, DATA_DIR, MAIN_DATA, PACKAGE_DATA}; pub fn setup() -> ParentHandler { ParentHandler::new() @@ -79,10 +80,11 @@ async fn setup_init( ctx: &SetupContext, password: Option, init_phases: InitPhases, -) -> Result<(AccountInfo, PreInitNetController), Error> { - let InitResult { net_ctrl } = init(&ctx.config, init_phases).await?; +) -> Result<(AccountInfo, InitResult), Error> { + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; - let account = net_ctrl + let account = init_result + .net_ctrl .db .mutate(|m| { let mut account = AccountInfo::load(m)?; @@ -98,7 +100,7 @@ async fn setup_init( }) .await?; - Ok((account, net_ctrl)) + Ok((account, init_result)) } #[derive(Deserialize, Serialize, TS)] @@ -140,7 +142,7 @@ pub async fn attach( disk_phase.start(); let requires_reboot = crate::disk::main::import( &*disk_guid, - &setup_ctx.datadir, + DATA_DIR, if tokio::fs::metadata(REPAIR_DISK_PATH).await.is_ok() { RepairStrategy::Aggressive } else { @@ -155,7 +157,7 @@ pub async fn attach( .with_ctx(|_| (ErrorKind::Filesystem, REPAIR_DISK_PATH))?; } if requires_reboot.0 { - crate::disk::main::export(&*disk_guid, &setup_ctx.datadir).await?; + crate::disk::main::export(&*disk_guid, DATA_DIR).await?; return Err(Error::new( eyre!( "Errors were corrected with your disk, but the server must be restarted in order to proceed" @@ -167,7 +169,7 @@ pub async fn attach( let (account, net_ctrl) = setup_init(&setup_ctx, password, init_phases).await?; - let rpc_ctx = RpcContext::init(&setup_ctx.config, disk_guid, Some(net_ctrl), rpc_ctx_phases).await?; + let rpc_ctx = RpcContext::init(&setup_ctx.webserver, &setup_ctx.config, disk_guid, Some(net_ctrl), rpc_ctx_phases).await?; Ok(((&account).try_into()?, rpc_ctx)) })?; @@ -391,18 +393,13 @@ pub async fn execute_inner( crate::disk::main::create( &[start_os_logicalname], &pvscan().await?, - &ctx.datadir, + DATA_DIR, encryption_password, ) .await?, ); - let _ = crate::disk::main::import( - &*guid, - &ctx.datadir, - RepairStrategy::Preen, - encryption_password, - ) - .await?; + let _ = crate::disk::main::import(&*guid, DATA_DIR, RepairStrategy::Preen, encryption_password) + .await?; disk_phase.complete(); let progress = SetupExecuteProgress { @@ -456,9 +453,16 @@ async fn fresh_setup( db.put(&ROOT, &Database::init(&account)?).await?; drop(db); - let InitResult { net_ctrl } = init(&ctx.config, init_phases).await?; + let init_result = init(&ctx.webserver, &ctx.config, init_phases).await?; - let rpc_ctx = RpcContext::init(&ctx.config, guid, Some(net_ctrl), rpc_ctx_phases).await?; + let rpc_ctx = RpcContext::init( + &ctx.webserver, + &ctx.config, + guid, + Some(init_result), + rpc_ctx_phases, + ) + .await?; Ok(((&account).try_into()?, rpc_ctx)) } @@ -513,10 +517,10 @@ async fn migrate( ) .await?; - let main_transfer_args = ("/media/startos/migrate/main/", "/embassy-data/main/"); + let main_transfer_args = ("/media/startos/migrate/main/", formatcp!("{MAIN_DATA}/")); let package_data_transfer_args = ( "/media/startos/migrate/package-data/", - "/embassy-data/package-data/", + formatcp!("{PACKAGE_DATA}/"), ); let tmpdir = Path::new(package_data_transfer_args.0).join("tmp"); @@ -571,7 +575,14 @@ async fn migrate( let (account, net_ctrl) = setup_init(&ctx, Some(start_os_password), init_phases).await?; - let rpc_ctx = RpcContext::init(&ctx.config, guid, Some(net_ctrl), rpc_ctx_phases).await?; + let rpc_ctx = RpcContext::init( + &ctx.webserver, + &ctx.config, + guid, + Some(net_ctrl), + rpc_ctx_phases, + ) + .await?; Ok(((&account).try_into()?, rpc_ctx)) } diff --git a/core/startos/src/shutdown.rs b/core/startos/src/shutdown.rs index f6a984897..4e45f74c0 100644 --- a/core/startos/src/shutdown.rs +++ b/core/startos/src/shutdown.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use crate::context::RpcContext; @@ -7,7 +7,7 @@ use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH}; use crate::prelude::*; use crate::sound::SHUTDOWN; use crate::util::Invoke; -use crate::PLATFORM; +use crate::{DATA_DIR, PLATFORM}; #[derive(Debug, Clone)] pub struct Shutdown { @@ -87,7 +87,7 @@ pub async fn shutdown(ctx: RpcContext) -> Result<(), Error> { .await?; ctx.shutdown .send(Some(Shutdown { - export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())), + export_args: Some((ctx.disk_guid.clone(), Path::new(DATA_DIR).to_owned())), restart: false, })) .map_err(|_| ()) @@ -107,7 +107,7 @@ pub async fn restart(ctx: RpcContext) -> Result<(), Error> { .await?; ctx.shutdown .send(Some(Shutdown { - export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())), + export_args: Some((ctx.disk_guid.clone(), Path::new(DATA_DIR).to_owned())), restart: true, })) .map_err(|_| ()) diff --git a/core/startos/src/ssh.rs b/core/startos/src/ssh.rs index afc5a5bf1..0c52fa136 100644 --- a/core/startos/src/ssh.rs +++ b/core/startos/src/ssh.rs @@ -3,20 +3,23 @@ use std::path::Path; use clap::builder::ValueParserFactory; use clap::Parser; -use color_eyre::eyre::eyre; use imbl_value::InternedString; use models::FromStrParser; use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; +use tokio::fs::OpenOptions; +use tokio::process::Command; use tracing::instrument; use ts_rs::TS; use crate::context::{CliContext, RpcContext}; +use crate::hostname::Hostname; use crate::prelude::*; use crate::util::io::create_file; -use crate::util::serde::{display_serializable, HandlerExtSerde, WithIoFormat}; +use crate::util::serde::{display_serializable, HandlerExtSerde, Pem, WithIoFormat}; +use crate::util::Invoke; -pub const SSH_AUTHORIZED_KEYS_FILE: &str = "/home/start9/.ssh/authorized_keys"; +pub const SSH_DIR: &str = "/home/start9/.ssh"; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SshKeys(BTreeMap>); @@ -143,7 +146,7 @@ pub async fn add(ctx: RpcContext, AddParams { key }: AddParams) -> Result, result: Vec) { @@ -226,23 +229,90 @@ pub async fn list(ctx: RpcContext) -> Result, Error> { } #[instrument(skip_all)] -pub async fn sync_keys>(keys: &SshKeys, dest: P) -> Result<(), Error> { +pub async fn sync_keys>( + hostname: &Hostname, + privkey: &Pem, + pubkeys: &SshKeys, + ssh_dir: P, +) -> Result<(), Error> { use tokio::io::AsyncWriteExt; - let dest = dest.as_ref(); - let ssh_dir = dest.parent().ok_or_else(|| { - Error::new( - eyre!("SSH Key File cannot be \"/\""), - crate::ErrorKind::Filesystem, - ) - })?; + let ssh_dir = ssh_dir.as_ref(); if tokio::fs::metadata(ssh_dir).await.is_err() { tokio::fs::create_dir_all(ssh_dir).await?; } - let mut f = create_file(dest).await?; - for key in keys.0.values() { + + let id_alg = if privkey.0.algorithm().is_ed25519() { + "id_ed25519" + } else if privkey.0.algorithm().is_ecdsa() { + "id_ecdsa" + } else if privkey.0.algorithm().is_rsa() { + "id_rsa" + } else { + "id_unknown" + }; + + let privkey_path = ssh_dir.join(id_alg); + let mut f = OpenOptions::new() + .create(true) + .write(true) + .mode(0o600) + .open(&privkey_path) + .await + .with_ctx(|_| { + ( + ErrorKind::Filesystem, + lazy_format!("create {privkey_path:?}"), + ) + })?; + f.write_all(privkey.to_string().as_bytes()).await?; + f.write_all(b"\n").await?; + f.sync_all().await?; + let mut f = create_file(ssh_dir.join(id_alg).with_extension("pub")).await?; + f.write_all( + (privkey + .0 + .public_key() + .to_openssh() + .with_kind(ErrorKind::OpenSsh)? + + " start9@" + + &*hostname.0) + .as_bytes(), + ) + .await?; + f.write_all(b"\n").await?; + f.sync_all().await?; + + let mut f = create_file(ssh_dir.join("authorized_keys")).await?; + for key in pubkeys.0.values() { f.write_all(key.0.to_key_format().as_bytes()).await?; f.write_all(b"\n").await?; } + + Command::new("chown") + .arg("-R") + .arg("start9:startos") + .arg(ssh_dir) + .invoke(ErrorKind::Filesystem) + .await?; + + Ok(()) +} + +#[instrument(skip_all)] +pub async fn sync_pubkeys>(pubkeys: &SshKeys, ssh_dir: P) -> Result<(), Error> { + use tokio::io::AsyncWriteExt; + + let ssh_dir = ssh_dir.as_ref(); + if tokio::fs::metadata(ssh_dir).await.is_err() { + tokio::fs::create_dir_all(ssh_dir).await?; + } + + let mut f = create_file(ssh_dir.join("authorized_keys")).await?; + for key in pubkeys.0.values() { + f.write_all(key.0.to_key_format().as_bytes()).await?; + f.write_all(b"\n").await?; + } + Ok(()) } diff --git a/core/startos/src/status/mod.rs b/core/startos/src/status/mod.rs index 398797a5a..cf1ca0658 100644 --- a/core/startos/src/status/mod.rs +++ b/core/startos/src/status/mod.rs @@ -80,7 +80,7 @@ impl MainStatus { } } - pub fn backing_up(self) -> Self { + pub fn backing_up(&self) -> Self { MainStatus::BackingUp { on_complete: if self.running() { StartStop::Start diff --git a/core/startos/src/system.rs b/core/startos/src/system.rs index e64f30e98..123fdec42 100644 --- a/core/startos/src/system.rs +++ b/core/startos/src/system.rs @@ -1,12 +1,18 @@ use std::collections::BTreeSet; use std::fmt; +use std::sync::Arc; use chrono::Utc; use clap::Parser; use color_eyre::eyre::eyre; use futures::FutureExt; use imbl::vector; +use mail_send::mail_builder::{self, MessageBuilder}; +use mail_send::SmtpClientBuilder; use rpc_toolkit::{from_fn_async, Context, Empty, HandlerExt, ParentHandler}; +use rustls::crypto::CryptoProvider; +use rustls::RootCertStore; +use rustls_pki_types::CertificateDer; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use tokio::process::Command; use tokio::sync::broadcast::Receiver; @@ -24,6 +30,7 @@ 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; +use crate::{MAIN_DATA, PACKAGE_DATA}; pub fn experimental() -> ParentHandler { ParentHandler::new() @@ -802,10 +809,10 @@ pub async fn get_mem_info() -> Result { #[instrument(skip_all)] async fn get_disk_info() -> Result { - let package_used_task = get_used("/embassy-data/package-data"); - let package_available_task = get_available("/embassy-data/package-data"); - let os_used_task = get_used("/embassy-data/main"); - let os_available_task = get_available("/embassy-data/main"); + let package_used_task = get_used(PACKAGE_DATA); + let package_available_task = get_available(PACKAGE_DATA); + let os_used_task = get_used(MAIN_DATA); + let os_available_task = get_available(MAIN_DATA); let (package_used, package_available, os_used, os_available) = futures::try_join!( package_used_task, @@ -871,6 +878,93 @@ pub async fn clear_system_smtp(ctx: RpcContext) -> Result<(), Error> { } Ok(()) } +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Parser, TS)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +pub struct TestSmtpParams { + #[arg(long)] + pub server: String, + #[arg(long)] + pub port: u16, + #[arg(long)] + pub from: String, + #[arg(long)] + pub to: String, + #[arg(long)] + pub login: String, + #[arg(long)] + pub password: Option, +} +pub async fn test_smtp( + _: RpcContext, + TestSmtpParams { + server, + port, + from, + to, + login, + password, + }: TestSmtpParams, +) -> Result<(), Error> { + use rustls_pki_types::pem::PemObject; + + let Some(pass_val) = password else { + return Err(Error::new( + eyre!("mail-send requires a password"), + ErrorKind::InvalidRequest, + )); + }; + + let mut root_cert_store = RootCertStore::empty(); + let pem = tokio::fs::read("/etc/ssl/certs/ca-certificates.crt").await?; + for cert in CertificateDer::pem_slice_iter(&pem) { + root_cert_store.add_parsable_certificates([cert.with_kind(ErrorKind::OpenSsl)?]); + } + + let cfg = Arc::new( + rustls::ClientConfig::builder_with_provider(Arc::new( + rustls::crypto::ring::default_provider(), + )) + .with_safe_default_protocol_versions()? + .with_root_certificates(root_cert_store) + .with_no_client_auth(), + ); + let client = SmtpClientBuilder::new_with_tls_config(server, port, cfg) + .implicit_tls(false) + .credentials((login.split("@").next().unwrap().to_owned(), pass_val)); + + fn parse_address<'a>(addr: &'a str) -> mail_builder::headers::address::Address<'a> { + if addr.find("<").map_or(false, |start| { + addr.find(">").map_or(false, |end| start < end) + }) { + addr.split_once("<") + .map(|(name, addr)| (name.trim(), addr.strip_suffix(">").unwrap_or(addr))) + .unwrap() + .into() + } else { + addr.into() + } + } + + let message = MessageBuilder::new() + .from(parse_address(&from)) + .to(parse_address(&to)) + .subject("StartOS Test Email") + .text_body("This is a test email sent from your StartOS Server"); + client + .connect() + .await + .map_err(|e| { + Error::new( + eyre!("mail-send connection error: {:?}", e), + ErrorKind::Unknown, + ) + })? + .send(message) + .await + .map_err(|e| Error::new(eyre!("mail-send send error: {:?}", e), ErrorKind::Unknown))?; + Ok(()) +} #[tokio::test] #[ignore] diff --git a/core/startos/src/update/mod.rs b/core/startos/src/update/mod.rs index 51d8d77ae..7daa4f3b2 100644 --- a/core/startos/src/update/mod.rs +++ b/core/startos/src/update/mod.rs @@ -20,7 +20,7 @@ use ts_rs::TS; use crate::context::{CliContext, RpcContext}; use crate::disk::mount::filesystem::bind::Bind; use crate::disk::mount::filesystem::block_dev::BlockDev; -use crate::disk::mount::filesystem::efivarfs::{self, EfiVarFs}; +use crate::disk::mount::filesystem::efivarfs::EfiVarFs; use crate::disk::mount::filesystem::overlayfs::OverlayGuard; use crate::disk::mount::filesystem::MountType; use crate::disk::mount::guard::{GenericMountGuard, MountGuard, TmpMountGuard}; @@ -106,7 +106,7 @@ pub async fn update_system( .with_kind(ErrorKind::Database)?, ) .await; - while { + loop { let progress = ctx .db .peek() @@ -122,14 +122,22 @@ pub async fn update_system( )) .await .with_kind(ErrorKind::Network)?; - progress.is_some() - } { - sub.recv().await; + if progress.is_none() { + return ws.normal_close("complete").await; + } + tokio::select! { + _ = sub.recv() => (), + res = async { + loop { + if ws.recv().await.transpose().with_kind(ErrorKind::Network)?.is_none() { + return Ok(()) + } + } + } => { + return res + } + } } - - ws.normal_close("complete").await?; - - Ok::<_, Error>(()) } .await { diff --git a/core/startos/src/util/actor/background.rs b/core/startos/src/util/actor/background.rs index f37e10c14..7666cbf04 100644 --- a/core/startos/src/util/actor/background.rs +++ b/core/startos/src/util/actor/background.rs @@ -15,8 +15,13 @@ impl BackgroundJobQueue { }, ) } - pub fn add_job(&self, fut: impl Future + Send + 'static) { - let _ = self.0.send(fut.boxed()); + pub fn add_job(&self, fut: impl Future + Send + 'static) { + let _ = self.0.send( + async { + fut.await; + } + .boxed(), + ); } } diff --git a/core/startos/src/util/future.rs b/core/startos/src/util/future.rs index f40e847bf..c690f9754 100644 --- a/core/startos/src/util/future.rs +++ b/core/startos/src/util/future.rs @@ -1,11 +1,13 @@ use std::pin::Pin; use std::task::{Context, Poll}; -use futures::future::abortable; -use futures::stream::{AbortHandle, Abortable}; -use futures::Future; +use futures::future::{abortable, pending, BoxFuture, FusedFuture}; +use futures::stream::{AbortHandle, Abortable, BoxStream}; +use futures::{Future, FutureExt, Stream, StreamExt}; use tokio::sync::watch; +use crate::prelude::*; + #[pin_project::pin_project(PinnedDrop)] pub struct DropSignaling { #[pin] @@ -102,6 +104,60 @@ impl CancellationHandle { } } +#[derive(Default)] +pub struct Until<'a> { + streams: Vec>>, + async_fns: Vec BoxFuture<'a, Result<(), Error>> + Send + 'a>>, +} +impl<'a> Until<'a> { + pub fn new() -> Self { + Self::default() + } + + pub fn with_stream( + mut self, + stream: impl Stream> + Send + 'a, + ) -> Self { + self.streams.push(stream.boxed()); + self + } + + pub fn with_async_fn(mut self, mut f: F) -> Self + where + F: FnMut() -> Fut + Send + 'a, + Fut: Future> + FusedFuture + Send + 'a, + { + self.async_fns.push(Box::new(move || f().boxed())); + self + } + + pub async fn run> + Send>( + &mut self, + fut: Fut, + ) -> Result<(), Error> { + let (res, _, _) = futures::future::select_all( + self.streams + .iter_mut() + .map(|s| { + async { + s.next().await.transpose()?.ok_or_else(|| { + Error::new(eyre!("stream is empty"), ErrorKind::Cancelled) + }) + } + .boxed() + }) + .chain(self.async_fns.iter_mut().map(|f| f())) + .chain([async { + fut.await?; + pending().await + } + .boxed()]), + ) + .await; + res + } +} + #[tokio::test] async fn test_cancellable() { use std::sync::Arc; diff --git a/core/startos/src/util/io.rs b/core/startos/src/util/io.rs index 0e7aada54..f0bae7a0a 100644 --- a/core/startos/src/util/io.rs +++ b/core/startos/src/util/io.rs @@ -15,7 +15,7 @@ use futures::future::{BoxFuture, Fuse}; use futures::{AsyncSeek, FutureExt, Stream, TryStreamExt}; use helpers::NonDetachingJoinHandle; use nix::unistd::{Gid, Uid}; -use tokio::fs::File; +use tokio::fs::{File, OpenOptions}; use tokio::io::{ duplex, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream, ReadBuf, WriteHalf, }; @@ -460,18 +460,30 @@ impl BackTrackingIO { } } } - pub fn rewind(&mut self) -> Vec { + pub fn rewind<'a>(&'a mut self) -> (Vec, &'a [u8]) { match std::mem::take(&mut self.buffer) { BTBuffer::Buffering { read, write } => { self.buffer = BTBuffer::Rewound { read: Cursor::new(read), }; - write + ( + write, + match &self.buffer { + BTBuffer::Rewound { read } => read.get_ref(), + _ => unreachable!(), + }, + ) } - BTBuffer::NotBuffering => Vec::new(), + BTBuffer::NotBuffering => (Vec::new(), &[]), BTBuffer::Rewound { read } => { self.buffer = BTBuffer::Rewound { read }; - Vec::new() + ( + Vec::new(), + match &self.buffer { + BTBuffer::Rewound { read } => read.get_ref(), + _ => unreachable!(), + }, + ) } } } @@ -529,7 +541,6 @@ impl std::io::Read for BackTrackingIO { } BTBuffer::NotBuffering => self.io.read(buf), BTBuffer::Rewound { read } => { - let mut ready = false; if (read.position() as usize) < read.get_ref().len() { let n = std::io::Read::read(read, buf)?; if n != 0 { @@ -923,6 +934,21 @@ pub async fn create_file(path: impl AsRef) -> Result { .with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("create {path:?}"))) } +pub async fn append_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + if let Some(parent) = path.parent() { + tokio::fs::create_dir_all(parent) + .await + .with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("mkdir -p {parent:?}")))?; + } + OpenOptions::new() + .create(true) + .append(true) + .open(path) + .await + .with_ctx(|_| (ErrorKind::Filesystem, lazy_format!("create {path:?}"))) +} + pub async fn delete_file(path: impl AsRef) -> Result<(), Error> { let path = path.as_ref(); tokio::fs::remove_file(path) diff --git a/core/startos/src/util/logger.rs b/core/startos/src/util/logger.rs index c464b328d..816721d5b 100644 --- a/core/startos/src/util/logger.rs +++ b/core/startos/src/util/logger.rs @@ -1,13 +1,62 @@ -use std::io; +use std::fs::File; +use std::io::{self, Write}; +use std::sync::{Arc, Mutex, MutexGuard}; +use lazy_static::lazy_static; use tracing::Subscriber; +use tracing_subscriber::fmt::MakeWriter; use tracing_subscriber::util::SubscriberInitExt; -#[derive(Clone)] -pub struct EmbassyLogger {} +lazy_static! { + pub static ref LOGGER: StartOSLogger = StartOSLogger::init(); +} -impl EmbassyLogger { - fn base_subscriber() -> impl Subscriber { +#[derive(Clone)] +pub struct StartOSLogger { + logfile: LogFile, +} + +#[derive(Clone, Default)] +struct LogFile(Arc>>); +impl<'a> MakeWriter<'a> for LogFile { + type Writer = Box; + fn make_writer(&'a self) -> Self::Writer { + let f = self.0.lock().unwrap(); + if f.is_some() { + struct TeeWriter<'a>(MutexGuard<'a, Option>); + impl<'a> Write for TeeWriter<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + let n = if let Some(f) = &mut *self.0 { + f.write(buf)? + } else { + buf.len() + }; + io::stderr().write_all(&buf[..n])?; + Ok(n) + } + fn flush(&mut self) -> io::Result<()> { + if let Some(f) = &mut *self.0 { + f.flush()?; + } + Ok(()) + } + } + Box::new(TeeWriter(f)) + } else { + drop(f); + Box::new(io::stderr()) + } + } +} + +impl StartOSLogger { + pub fn enable(&self) {} + + pub fn set_logfile(&self, logfile: Option) { + *self.logfile.0.lock().unwrap() = logfile; + } + + fn base_subscriber(logfile: LogFile) -> impl Subscriber { use tracing_error::ErrorLayer; use tracing_subscriber::prelude::*; use tracing_subscriber::{fmt, EnvFilter}; @@ -24,7 +73,7 @@ impl EmbassyLogger { .add_directive("tokio=trace".parse().unwrap()) .add_directive("runtime=trace".parse().unwrap()); let fmt_layer = fmt::layer() - .with_writer(io::stderr) + .with_writer(logfile) .with_line_number(true) .with_file(true) .with_target(true); @@ -39,11 +88,12 @@ impl EmbassyLogger { sub } - pub fn init() -> Self { - Self::base_subscriber().init(); + fn init() -> Self { + let logfile = LogFile::default(); + Self::base_subscriber(logfile.clone()).init(); color_eyre::install().unwrap_or_else(|_| tracing::warn!("tracing too many times")); - EmbassyLogger {} + StartOSLogger { logfile } } } diff --git a/core/startos/src/util/net.rs b/core/startos/src/util/net.rs index 9e1beeaba..1189f70f2 100644 --- a/core/startos/src/util/net.rs +++ b/core/startos/src/util/net.rs @@ -19,13 +19,8 @@ pub trait WebSocketExt { } impl WebSocketExt for ws::WebSocket { - async fn normal_close(mut self, msg: impl Into> + Send) -> Result<(), Error> { - self.send(ws::Message::Close(Some(CloseFrame { - code: 1000, - reason: msg.into(), - }))) - .await - .with_kind(ErrorKind::Network) + async fn normal_close(self, msg: impl Into> + Send) -> Result<(), Error> { + self.close_result(Ok::<_, Error>(msg)).await } async fn close_result( mut self, @@ -38,15 +33,23 @@ impl WebSocketExt for ws::WebSocket { reason: msg.into(), }))) .await - .with_kind(ErrorKind::Network), + .with_kind(ErrorKind::Network)?, Err(e) => self .send(ws::Message::Close(Some(CloseFrame { code: 1011, reason: e.to_string().into(), }))) .await - .with_kind(ErrorKind::Network), + .with_kind(ErrorKind::Network)?, } + while !matches!( + self.recv() + .await + .transpose() + .with_kind(ErrorKind::Network)?, + Some(ws::Message::Close(_)) | None + ) {} + Ok(()) } } diff --git a/core/startos/src/util/rpc.rs b/core/startos/src/util/rpc.rs index b2dea340e..f7c91eb82 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, HandlerExt, ParentHandler}; use serde::{Deserialize, Serialize}; -use url::Url; use crate::context::CliContext; use crate::prelude::*; diff --git a/core/startos/src/util/rpc_client.rs b/core/startos/src/util/rpc_client.rs index fc93e4c64..82ce11e20 100644 --- a/core/startos/src/util/rpc_client.rs +++ b/core/startos/src/util/rpc_client.rs @@ -47,7 +47,7 @@ impl RpcClient { let mut lines = BufReader::new(reader).lines(); while let Some(line) = lines.next_line().await.transpose() { match line.map_err(Error::from).and_then(|l| { - serde_json::from_str::(dbg!(&l)) + serde_json::from_str::(crate::dbg!(&l)) .with_kind(ErrorKind::Deserialization) }) { Ok(l) => { @@ -114,7 +114,7 @@ impl RpcClient { let (send, recv) = oneshot::channel(); w.lock().await.insert(id.clone(), send); self.writer - .write_all((dbg!(serde_json::to_string(&request))? + "\n").as_bytes()) + .write_all((crate::dbg!(serde_json::to_string(&request))? + "\n").as_bytes()) .await .map_err(|e| { let mut err = rpc_toolkit::yajrc::INTERNAL_ERROR.clone(); @@ -154,7 +154,7 @@ impl RpcClient { params, }; self.writer - .write_all((dbg!(serde_json::to_string(&request))? + "\n").as_bytes()) + .write_all((crate::dbg!(serde_json::to_string(&request))? + "\n").as_bytes()) .await .map_err(|e| { let mut err = rpc_toolkit::yajrc::INTERNAL_ERROR.clone(); diff --git a/core/startos/src/util/sync.rs b/core/startos/src/util/sync.rs index 1edd21ce1..d6099cb5b 100644 --- a/core/startos/src/util/sync.rs +++ b/core/startos/src/util/sync.rs @@ -1,3 +1,8 @@ +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Poll, Waker}; + +#[derive(Debug, Default)] pub struct SyncMutex(std::sync::Mutex); impl SyncMutex { pub fn new(t: T) -> Self { @@ -10,3 +15,148 @@ impl SyncMutex { f(&*self.0.lock().unwrap()) } } + +struct WatchShared { + version: u64, + data: T, + wakers: Vec, +} +impl WatchShared { + fn modified(&mut self) { + self.version += 1; + for waker in self.wakers.drain(..) { + waker.wake(); + } + } +} + +#[pin_project::pin_project] +pub struct Watch { + shared: Arc>>, + version: u64, +} +impl Clone for Watch { + fn clone(&self) -> Self { + Self { + shared: self.shared.clone(), + version: self.version, + } + } +} +impl Watch { + pub fn new(init: T) -> Self { + Self { + shared: Arc::new(SyncMutex::new(WatchShared { + version: 1, + data: init, + wakers: Vec::new(), + })), + version: 0, + } + } + pub fn clone_unseen(&self) -> Self { + Self { + shared: self.shared.clone(), + version: 0, + } + } + pub fn poll_changed(&mut self, cx: &mut std::task::Context<'_>) -> Poll<()> { + self.shared.mutate(|shared| { + if shared.version != self.version { + self.version = shared.version; + Poll::Ready(()) + } else { + let waker = cx.waker(); + if !shared.wakers.iter().any(|w| w.will_wake(waker)) { + shared.wakers.push(waker.clone()); + } + Poll::Pending + } + }) + } + pub async fn changed(&mut self) { + futures::future::poll_fn(|cx| self.poll_changed(cx)).await + } + pub async fn wait_for bool>(&mut self, mut f: F) { + loop { + if self.peek(&mut f) { + break; + } + self.changed().await; + } + } + pub fn send_if_modified bool>(&self, modify: F) -> bool { + self.shared.mutate(|shared| { + let changed = modify(&mut shared.data); + if changed { + shared.modified(); + } + changed + }) + } + pub fn send_modify U>(&self, modify: F) -> U { + self.shared.mutate(|shared| { + let res = modify(&mut shared.data); + shared.modified(); + res + }) + } + pub fn send_replace(&self, new: T) -> T { + self.send_modify(|a| std::mem::replace(a, new)) + } + pub fn send(&self, new: T) { + self.send_replace(new); + } + pub fn mark_changed(&self) { + self.shared.mutate(|shared| shared.modified()) + } + pub fn mark_unseen(&mut self) { + self.version = 0; + } + pub fn mark_seen(&mut self) { + self.shared.peek(|shared| { + self.version = shared.version; + }) + } + pub fn peek U>(&self, f: F) -> U { + self.shared.peek(|shared| f(&shared.data)) + } + pub fn peek_and_mark_seen U>(&mut self, f: F) -> U { + self.shared.peek(|shared| { + self.version = shared.version; + f(&shared.data) + }) + } + pub fn peek_mut U>(&self, f: F) -> U { + self.shared.mutate(|shared| f(&mut shared.data)) + } +} +impl Watch { + pub fn read(&self) -> T { + self.peek(|a| a.clone()) + } +} +impl futures::Stream for Watch { + type Item = T; + fn poll_next( + self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + let this = self.project(); + this.shared.mutate(|shared| { + if shared.version != *this.version { + *this.version = shared.version; + Poll::Ready(Some(shared.data.clone())) + } else { + let waker = cx.waker(); + if !shared.wakers.iter().any(|w| w.will_wake(waker)) { + shared.wakers.push(waker.clone()); + } + Poll::Pending + } + }) + } + fn size_hint(&self) -> (usize, Option) { + (1, None) + } +} diff --git a/core/startos/src/version/mod.rs b/core/startos/src/version/mod.rs index 94b61176b..2ff43fec2 100644 --- a/core/startos/src/version/mod.rs +++ b/core/startos/src/version/mod.rs @@ -7,7 +7,7 @@ use futures::future::BoxFuture; use futures::{Future, FutureExt}; use imbl::Vector; use imbl_value::{to_value, InternedString}; -use patch_db::json_ptr::{JsonPointer, ROOT}; +use patch_db::json_ptr::ROOT; use crate::context::RpcContext; use crate::db::model::Database; @@ -29,7 +29,12 @@ mod v0_3_6_alpha_7; mod v0_3_6_alpha_8; mod v0_3_6_alpha_9; -pub type Current = v0_3_6_alpha_9::Version; // VERSION_BUMP +mod v0_3_6_alpha_10; +mod v0_3_6_alpha_11; +mod v0_3_6_alpha_12; +mod v0_3_6_alpha_13; + +pub type Current = v0_3_6_alpha_13::Version; // VERSION_BUMP impl Current { #[instrument(skip(self, db))] @@ -52,7 +57,7 @@ impl Current { let pre_ups = PreUps::load(&from, &self).await?; db.apply_function(|mut db| { migrate_from_unchecked(&from, &self, pre_ups, &mut db)?; - Ok::<_, Error>((db, ())) + Ok::<_, Error>((to_value(&from_value::(db.clone())?)?, ())) }) .await?; } @@ -62,31 +67,44 @@ impl Current { } } -pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> { - let mut peek; - while let Some(version) = { - peek = ctx.db.peek().await; - peek.as_public() - .as_server_info() - .as_post_init_migration_todos() - .de()? - .first() - .cloned() - .map(Version::from_exver_version) - .as_ref() - .map(Version::as_version_t) - .transpose()? - } { - version.0.post_up(ctx).await?; - ctx.db - .mutate(|db| { - db.as_public_mut() - .as_server_info_mut() - .as_post_init_migration_todos_mut() - .mutate(|m| Ok(m.remove(&version.0.semver()))) - }) - .await?; +pub async fn post_init( + ctx: &RpcContext, + mut progress: PhaseProgressTrackerHandle, +) -> Result<(), Error> { + let mut peek = ctx.db.peek().await; + let todos = peek + .as_public() + .as_server_info() + .as_post_init_migration_todos() + .de()?; + if !todos.is_empty() { + progress.set_total(todos.len() as u64); + while let Some(version) = { + peek = ctx.db.peek().await; + peek.as_public() + .as_server_info() + .as_post_init_migration_todos() + .de()? + .first() + .cloned() + .map(Version::from_exver_version) + .as_ref() + .map(Version::as_version_t) + .transpose()? + } { + version.0.post_up(ctx).await?; + ctx.db + .mutate(|db| { + db.as_public_mut() + .as_server_info_mut() + .as_post_init_migration_todos_mut() + .mutate(|m| Ok(m.remove(&version.0.semver()))) + }) + .await?; + progress += 1; + } } + progress.complete(); Ok(()) } @@ -108,6 +126,10 @@ enum Version { V0_3_6_alpha_7(Wrapper), V0_3_6_alpha_8(Wrapper), V0_3_6_alpha_9(Wrapper), + V0_3_6_alpha_10(Wrapper), + V0_3_6_alpha_11(Wrapper), + V0_3_6_alpha_12(Wrapper), + V0_3_6_alpha_13(Wrapper), // VERSION_BUMP Other(exver::Version), } @@ -141,6 +163,10 @@ impl Version { Self::V0_3_6_alpha_7(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_8(v) => DynVersion(Box::new(v.0)), Self::V0_3_6_alpha_9(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_10(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_11(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_12(v) => DynVersion(Box::new(v.0)), + Self::V0_3_6_alpha_13(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP Self::Other(v) => { return Err(Error::new( eyre!("unknown version {v}"), @@ -166,6 +192,10 @@ impl Version { Version::V0_3_6_alpha_7(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_8(Wrapper(x)) => x.semver(), Version::V0_3_6_alpha_9(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_10(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_11(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_12(Wrapper(x)) => x.semver(), + Version::V0_3_6_alpha_13(Wrapper(x)) => x.semver(), // VERSION_BUMP Version::Other(x) => x.clone(), } } diff --git a/core/startos/src/version/update_details/v0_3_6.md b/core/startos/src/version/update_details/v0_3_6.md index b93e8ad1a..177845f08 100644 --- a/core/startos/src/version/update_details/v0_3_6.md +++ b/core/startos/src/version/update_details/v0_3_6.md @@ -17,17 +17,16 @@ StartOS v0.3.6 is a complete rewrite of the OS internals (everything you don't s ## Changelog - [Switch to lxc-based container runtime](#lxc) -- [Update s9pk archive format](#new-s9pk-archive-format) -- [Improve config](#better-config) -- [Unify Actions](#unify-actions) +- [Update s9pk archive format](#s9pk-archive-format) +- [Improve Actions](#actions) - [Use squashfs images for OS updates](#squashfs-updates) -- [Introduce Typescript package API and SDK](#typescript-package-api-and-sdk) +- [Introduce Typescript package API and SDK](#typescript-sdk) - [Remove Postgresql](#remove-postgressql) - [Implement detailed progress reporting](#progress-reporting) - [Improve registry protocol](#registry-protocol) - [Replace unique .local URLs with unique ports](#lan-port-forwarding) - [Use start-fs Fuse module for improved backups](#improved-backups) -- [Switch to Exver for versioning](#Exver) +- [Switch to Exver for versioning](#exver) - [Support clearnet hosting via start-cli](#clearnet) ### LXC @@ -38,21 +37,17 @@ StartOS now uses a nested container paradigm based on LXC for the outer containe The S9PK archive format has been overhauled to allow for signature verification of partial downloads, and allow direct mounting of container images without unpacking the s9pk. -### Better config - -Expanded support for input types and a new UI makes configuring services easier and more powerful. - ### Actions -Actions take arbitrary form input _and_ return arbitrary responses, thus satisfying the needs of both Config and Properties, which will be removed in a future release. This gives packages developers the ability to break up Config and Properties into smaller, more specific formats, or to exclude them entirely without polluting the UI. +Actions take arbitrary form input and return arbitrary responses, thus satisfying the needs of both Config and Properties, which have been removed. The new actions API gives packages developers the ability to break up Config and Properties into smaller, more specific formats, or to exclude them entirely without polluting the UI. Improved form design and new input types round out the actions experience. ### Squashfs updates StartOS now uses squashfs images to represent OS updates. This allows for better update verification, and improved reliability over rsync updates. -### Typescript package API and SDK +### Typescript SDK -StartOS now exposes a Typescript API. Package developers can take advantage in a simple, typesafe way using the new start-sdk. A barebones StartOS package (s9pk) can be produced in minutes with minimal knowledge or skill. More advanced developers can use the SDK to create highly customized user experiences with their service. +Package developers can now take advantage of StartOS APIs using the new start-sdk, available in Typescript. A barebones StartOS package (s9pk) can be produced in minutes with minimal knowledge or skill. More advanced developers can use the SDK to create highly customized user experiences with their service. ### Remove PostgresSQL @@ -76,8 +71,14 @@ The new start-fs fuse module unifies file system expectations for various platfo ### Exver -StartOS now uses Extended Versioning (Exver), which consists of three parts, separated by semicolons: (1) a Semver-compliant upstream version, (2) a Semver-compliant wrapper version, and (3) an optional "flavor" prefix. Flavors can be thought of as alternative implementations of services, where a user would only want one or the other installed, and data can feasibly be migrating beetween the two. Another common characteristic of flavors is that they satisfy the same API requirement of dependents, though this is not strictly necessary. A valid Exver looks something like this: `#knots:28.0.:1.0-beta.1`. This would translate to "the first beta release of StartOS wrapper version 1.0 of Bitcoin Knots version 27.0". +StartOS now uses Extended Versioning (Exver), which consists of three parts: (1) a Semver-compliant upstream version, (2) a Semver-compliant wrapper version, and (3) an optional "flavor" prefix. Flavors can be thought of as alternative implementations of services, where a user would only want one or the other installed, and data can feasibly be migrating between the two. Another common characteristic of flavors is that they satisfy the same API requirement of dependents, though this is not strictly necessary. A valid Exver looks something like this: `#knots:28.0.:1.0-beta.1`. This would translate to "the first beta release of StartOS wrapper version 1.0 of Bitcoin Knots version 27.0". ### Clearnet -It is now possible, and quite easy, to expose specific services interfaces to the public Internet on a standard domain using start-cli. This functionality will be expanded upon and moved into the StartOS UI in a future release. +It is now possible, and quite easy, to expose service interfaces to the public Internet on a standard domain using start-cli. In addition to choosing which service interfaces to expose on which domains/subdomains, users have two options: + +1. Open ports on their router. This option is free and easy to accomplish with most routers. The drawback is that the user's home IP address is revealed to anyone accessing the exposes resources. For example, hosting a blog in this way would reveal your home IP address, and therefor your approximate location on Earth, to your readers. + +2. Use a Wireguard VPN to proxy web traffic. This option requires the user to provision a $5-$10/month remote VPS and perform a few, simple commands. The result is the successful obfuscation of the users home IP address. + +The CLI-driven clearnet functionality will be expanded upon and moved into the main StartOS UI in a future release. diff --git a/core/startos/src/version/v0_3_6_alpha_0.rs b/core/startos/src/version/v0_3_6_alpha_0.rs index 7a6045a3a..6ed3fc316 100644 --- a/core/startos/src/version/v0_3_6_alpha_0.rs +++ b/core/startos/src/version/v0_3_6_alpha_0.rs @@ -1,19 +1,16 @@ use std::collections::BTreeMap; -use std::future::Future; use std::path::Path; use chrono::{DateTime, Utc}; +use const_format::formatcp; use ed25519_dalek::SigningKey; use exver::{PreReleaseSegment, VersionRange}; use imbl_value::{json, InternedString}; -use itertools::Itertools; use models::PackageId; -use openssl::pkey::{PKey, Private}; +use openssl::pkey::PKey; use openssl::x509::X509; -use patch_db::ModelExt; use sqlx::postgres::PgConnectOptions; use sqlx::{PgPool, Row}; -use ssh_key::Fingerprint; use tokio::process::Command; use torut::onion::TorSecretKeyV3; @@ -23,15 +20,11 @@ use crate::account::AccountInfo; use crate::auth::Sessions; use crate::backup::target::cifs::CifsTargets; use crate::context::RpcContext; -use crate::db::model::Database; use crate::disk::mount::filesystem::cifs::Cifs; use crate::disk::mount::util::unmount; use crate::hostname::Hostname; use crate::net::forward::AvailablePorts; use crate::net::keys::KeyStore; -use crate::net::ssl::CertStore; -use crate::net::tor; -use crate::net::tor::OnionStore; use crate::notifications::{Notification, Notifications}; use crate::prelude::*; use crate::s9pk::merkle_archive::source::multi_cursor_file::MultiCursorFile; @@ -39,6 +32,7 @@ use crate::ssh::{SshKeys, SshPubKey}; use crate::util::crypto::ed25519_expand_key; use crate::util::serde::{Pem, PemEncoding}; use crate::util::Invoke; +use crate::{DATA_DIR, PACKAGE_DATA}; lazy_static::lazy_static! { static ref V0_3_6_alpha_0: exver::Version = exver::Version::new( @@ -191,7 +185,6 @@ async fn init_postgres(datadir: impl AsRef) -> Result { .run(&secret_store) .await .with_kind(crate::ErrorKind::Database)?; - dbg!("Init Postgres Done"); Ok(secret_store) } @@ -200,7 +193,7 @@ pub struct Version; impl VersionT for Version { type Previous = v0_3_5_2::Version; - type PreUpRes = (AccountInfo, SshKeys, CifsTargets, Notifications); + type PreUpRes = (AccountInfo, SshKeys, CifsTargets); fn semver(self) -> exver::Version { V0_3_6_alpha_0.clone() } @@ -208,22 +201,16 @@ impl VersionT for Version { &V0_3_0_COMPAT } async fn pre_up(self) -> Result { - let pg = init_postgres("/embassy-data").await?; + let pg = init_postgres(DATA_DIR).await?; let account = previous_account_info(&pg).await?; let ssh_keys = previous_ssh_keys(&pg).await?; let cifs = previous_cifs(&pg).await?; - let notifications = previous_notifications(pg).await?; - - Ok((account, ssh_keys, cifs, notifications)) + Ok((account, ssh_keys, cifs)) } - fn up( - self, - db: &mut Value, - (account, ssh_keys, cifs, notifications): Self::PreUpRes, - ) -> Result<(), Error> { + fn up(self, db: &mut Value, (account, ssh_keys, cifs): Self::PreUpRes) -> Result<(), Error> { let wifi = json!({ "infterface": db["server-info"]["wifi"]["interface"], "ssids": db["server-info"]["wifi"]["ssids"], @@ -305,7 +292,7 @@ impl VersionT for Version { value["sshPubkeys"] = to_value(&ssh_keys)?; value["availablePorts"] = to_value(&AvailablePorts::new())?; value["sessions"] = to_value(&Sessions::new())?; - value["notifications"] = to_value(¬ifications)?; + value["notifications"] = to_value(&Notifications::new())?; value["cifs"] = to_value(&cifs)?; value["packageStores"] = json!({}); value @@ -315,7 +302,6 @@ impl VersionT for Version { "private": private, }); - dbg!("Should be done with the up"); *db = next; Ok(()) } @@ -329,7 +315,7 @@ impl VersionT for Version { #[instrument(skip(self, ctx))] /// MUST be idempotent, and is run after *all* db migrations async fn post_up(self, ctx: &RpcContext) -> Result<(), Error> { - let path = Path::new("/embassy-data/package-data/archive/"); + let path = Path::new(formatcp!("{PACKAGE_DATA}/archive/")); if !path.is_dir() { return Err(Error::new( eyre!( @@ -383,64 +369,6 @@ impl VersionT for Version { } } -async fn previous_notifications(pg: sqlx::Pool) -> Result { - let notification_cursor = sqlx::query(r#"SELECT * FROM notifications"#) - .fetch_all(&pg) - .await?; - let notifications = { - let mut notifications = Notifications::default(); - for row in notification_cursor { - let package_id = serde_json::from_str::( - row.try_get("package_id") - .with_ctx(|_| (ErrorKind::Database, "package_id"))?, - ) - .ok(); - - let created_at = row - .try_get("created_at") - .with_ctx(|_| (ErrorKind::Database, "created_at"))?; - let code = row - .try_get::("code") - .with_ctx(|_| (ErrorKind::Database, "code"))? as u32; - let id = row - .try_get::("id") - .with_ctx(|_| (ErrorKind::Database, "id"))? as u32; - let level = serde_json::from_str( - row.try_get("level") - .with_ctx(|_| (ErrorKind::Database, "level"))?, - ) - .with_kind(ErrorKind::Database) - .with_ctx(|_| (ErrorKind::Database, "level: serde_json "))?; - let title = row - .try_get("title") - .with_ctx(|_| (ErrorKind::Database, "title"))?; - let message = row - .try_get("message") - .with_ctx(|_| (ErrorKind::Database, "message"))?; - let data = serde_json::from_str( - row.try_get("data") - .with_ctx(|_| (ErrorKind::Database, "data"))?, - ) - .unwrap_or_default(); - - notifications.0.insert( - id, - Notification { - package_id, - created_at, - code, - level, - title, - message, - data, - }, - ); - } - notifications - }; - Ok(notifications) -} - #[tracing::instrument(skip_all)] async fn previous_cifs(pg: &sqlx::Pool) -> Result { let cifs = sqlx::query(r#"SELECT * FROM cifs_shares"#) @@ -448,16 +376,17 @@ async fn previous_cifs(pg: &sqlx::Pool) -> Result(( id, Cifs { hostname: row .try_get("hostname") .with_ctx(|_| (ErrorKind::Database, "hostname"))?, - path: serde_json::from_str(row.try_get("path")?) - .with_kind(ErrorKind::Database) - .with_ctx(|_| (ErrorKind::Database, "path"))?, + path: row + .try_get::("path") + .with_ctx(|_| (ErrorKind::Database, "path"))? + .into(), username: row .try_get("username") .with_ctx(|_| (ErrorKind::Database, "username"))?, @@ -486,7 +415,7 @@ async fn previous_account_info(pg: &sqlx::Pool) -> Result>, _>("tor_key") .with_ctx(|_| (ErrorKind::Database, "tor_key"))? @@ -511,7 +440,7 @@ async fn previous_account_info(pg: &sqlx::Pool) -> Result Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_10.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + for (_, package) in db["public"]["packageData"] + .as_object_mut() + .ok_or_else(|| { + Error::new( + eyre!("expected public.packageData to be an object"), + ErrorKind::Database, + ) + })? + .iter_mut() + { + for (_, host) in package["hosts"] + .as_object_mut() + .ok_or_else(|| { + Error::new( + eyre!("expected public.packageData[id].hosts to be an object"), + ErrorKind::Database, + ) + })? + .iter_mut() + { + let mut onions = BTreeSet::new(); + let mut domains = BTreeMap::new(); + let addresses = from_value::>(host["addresses"].clone())?; + for address in addresses { + match address { + HostAddress::Onion { address } => { + onions.insert(address); + } + HostAddress::Domain { address } => { + domains.insert( + address, + DomainConfig { + public: true, + acme: None, + }, + ); + } + } + } + host["onions"] = to_value(&onions)?; + host["domains"] = to_value(&domains)?; + } + } + + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/core/startos/src/version/v0_3_6_alpha_11.rs b/core/startos/src/version/v0_3_6_alpha_11.rs new file mode 100644 index 000000000..8a6dedd6e --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_11.rs @@ -0,0 +1,83 @@ +use exver::{PreReleaseSegment, VersionRange}; +use imbl_value::json; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_10, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_11: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 11.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_10::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_11.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + let acme = std::mem::replace( + &mut db["public"]["serverInfo"]["acme"], + Value::Object(Default::default()), + ); + if !acme.is_null() && acme["provider"].as_str().is_some() { + db["public"]["serverInfo"]["acme"] + [&acme["provider"].as_str().or_not_found("provider")?] = + json!({ "contact": &acme["contact"] }); + } + + for (_, package) in db["public"]["packageData"] + .as_object_mut() + .ok_or_else(|| { + Error::new( + eyre!("expected public.packageData to be an object"), + ErrorKind::Database, + ) + })? + .iter_mut() + { + for (_, host) in package["hosts"] + .as_object_mut() + .ok_or_else(|| { + Error::new( + eyre!("expected public.packageData[id].hosts to be an object"), + ErrorKind::Database, + ) + })? + .iter_mut() + { + for (_, bind) in host["bindings"] + .as_object_mut() + .ok_or_else(|| { + Error::new( + eyre!("expected public.packageData[id].hosts[hostId].bindings to be an object"), + ErrorKind::Database, + ) + })? + .iter_mut() + { + bind["net"] = bind["lan"].clone(); + bind["net"]["public"] = Value::Bool(false); + } + } + } + + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/core/startos/src/version/v0_3_6_alpha_12.rs b/core/startos/src/version/v0_3_6_alpha_12.rs new file mode 100644 index 000000000..3508a27ac --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_12.rs @@ -0,0 +1,68 @@ +use std::collections::BTreeMap; + +use exver::{PreReleaseSegment, VersionRange}; +use imbl_value::json; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_11, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_12: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 12.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_11::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_12.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + let bindings: BTreeMap = [( + 80, + json!({ + "enabled": false, + "options": { + "preferredExternalPort": 80, + "addSsl": { + "preferredExternalPort": 443, + "alpn": { "specified": [ "http/1.1", "h2" ] }, + }, + "secure": null, + }, + "net": { + "assignedPort": null, + "assignedSslPort": 443, + "public": false, + } + }), + )] + .into_iter() + .collect(); + let onion = db["public"]["serverInfo"]["onionAddress"].clone(); + db["public"]["serverInfo"]["host"] = json!({ + "bindings": bindings, + "onions": [onion], + "domains": {}, + "hostnameInfo": {}, + }); + + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/core/startos/src/version/v0_3_6_alpha_13.rs b/core/startos/src/version/v0_3_6_alpha_13.rs new file mode 100644 index 000000000..da34f7743 --- /dev/null +++ b/core/startos/src/version/v0_3_6_alpha_13.rs @@ -0,0 +1,39 @@ +use std::collections::BTreeMap; + +use exver::{PreReleaseSegment, VersionRange}; +use imbl_value::json; + +use super::v0_3_5::V0_3_0_COMPAT; +use super::{v0_3_6_alpha_12, VersionT}; +use crate::prelude::*; + +lazy_static::lazy_static! { + static ref V0_3_6_alpha_13: exver::Version = exver::Version::new( + [0, 3, 6], + [PreReleaseSegment::String("alpha".into()), 13.into()] + ); +} + +#[derive(Clone, Copy, Debug, Default)] +pub struct Version; + +impl VersionT for Version { + type Previous = v0_3_6_alpha_12::Version; + type PreUpRes = (); + + async fn pre_up(self) -> Result { + Ok(()) + } + fn semver(self) -> exver::Version { + V0_3_6_alpha_13.clone() + } + fn compat(self) -> &'static VersionRange { + &V0_3_0_COMPAT + } + fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + Ok(()) + } + fn down(self, _db: &mut Value) -> Result<(), Error> { + Ok(()) + } +} diff --git a/core/startos/src/version/v0_3_6_alpha_6.rs b/core/startos/src/version/v0_3_6_alpha_6.rs index d91caa82b..7d62773ea 100644 --- a/core/startos/src/version/v0_3_6_alpha_6.rs +++ b/core/startos/src/version/v0_3_6_alpha_6.rs @@ -27,7 +27,7 @@ impl VersionT for Version { async fn pre_up(self) -> Result { Ok(()) } - fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { + fn up(self, _db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> { Ok(()) } async fn post_up<'a>(self, ctx: &'a crate::context::RpcContext) -> Result<(), Error> { diff --git a/core/startos/src/version/v0_3_6_alpha_7.rs b/core/startos/src/version/v0_3_6_alpha_7.rs index bbf9468ff..baf7aaa35 100644 --- a/core/startos/src/version/v0_3_6_alpha_7.rs +++ b/core/startos/src/version/v0_3_6_alpha_7.rs @@ -1,5 +1,5 @@ use exver::{PreReleaseSegment, VersionRange}; -use imbl_value::{json, InOMap}; +use imbl_value::json; use tokio::process::Command; use super::v0_3_5::V0_3_0_COMPAT; diff --git a/core/startos/src/version/v0_3_6_alpha_8.rs b/core/startos/src/version/v0_3_6_alpha_8.rs index fcef96a69..99920c767 100644 --- a/core/startos/src/version/v0_3_6_alpha_8.rs +++ b/core/startos/src/version/v0_3_6_alpha_8.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use exver::{PreReleaseSegment, VersionRange}; use tokio::fs::File; @@ -12,6 +14,7 @@ use crate::s9pk::v2::SIG_CONTEXT; use crate::s9pk::S9pk; use crate::service::LoadDisposition; use crate::util::io::create_file; +use crate::DATA_DIR; lazy_static::lazy_static! { static ref V0_3_6_alpha_8: exver::Version = exver::Version::new( @@ -40,7 +43,7 @@ impl VersionT for Version { Ok(()) } async fn post_up(self, ctx: &crate::context::RpcContext) -> Result<(), Error> { - let s9pk_dir = ctx.datadir.join(PKG_ARCHIVE_DIR).join("installed"); + let s9pk_dir = Path::new(DATA_DIR).join(PKG_ARCHIVE_DIR).join("installed"); if tokio::fs::metadata(&s9pk_dir).await.is_ok() { let mut read_dir = tokio::fs::read_dir(&s9pk_dir).await?; diff --git a/core/startos/src/volume.rs b/core/startos/src/volume.rs index c7165b202..113802286 100644 --- a/core/startos/src/volume.rs +++ b/core/startos/src/volume.rs @@ -1,10 +1,9 @@ use std::path::{Path, PathBuf}; pub use helpers::script_dir; +use models::PackageId; pub use models::VolumeId; -use models::{HostId, PackageId}; -use crate::net::PACKAGE_CERT_PATH; use crate::prelude::*; use crate::util::VersionString; @@ -36,7 +35,3 @@ pub fn asset_dir>( pub fn backup_dir(pkg_id: &PackageId) -> PathBuf { Path::new(BACKUP_DIR).join(pkg_id).join("data") } - -pub fn cert_dir(pkg_id: &PackageId, host_id: &HostId) -> PathBuf { - Path::new(PACKAGE_CERT_PATH).join(pkg_id).join(host_id) -} diff --git a/debian/postinst b/debian/postinst index 3714df8d4..d29fcfb86 100755 --- a/debian/postinst +++ b/debian/postinst @@ -26,8 +26,12 @@ if [ -f /etc/default/grub ]; then fi # set local and remote login prompt -echo "StartOS v$(cat /usr/lib/startos/VERSION.txt) [\m] on \n.local (\l)" > /etc/issue -echo "StartOS v$(cat /usr/lib/startos/VERSION.txt)" > /etc/issue.net +cat << EOF > /etc/issue +StartOS v$(cat /usr/lib/startos/VERSION.txt) [\\m] on \\n.local (\\l) +EOF +cat << EOF > /etc/issue.net +StartOS v$(cat /usr/lib/startos/VERSION.txt) +EOF # change timezone rm -f /etc/localtime @@ -86,6 +90,8 @@ 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 sed -i 's/\(#\|\^\)\s*\([^=]\+\)=\(suspend\|hibernate\)\s*$/\2=ignore/g' /etc/systemd/logind.conf sed -i '/\(^\|#\)MulticastDNS=/c\MulticastDNS=no' /etc/systemd/resolved.conf +sed -i 's/\[Service\]/[Service]\nEnvironment=SYSTEMD_LOG_LEVEL=debug/' /lib/systemd/system/systemd-timesyncd.service +sed -i '/\(^\|#\)RootDistanceMaxSec=/c\RootDistanceMaxSec=10' /etc/systemd/timesyncd.conf mkdir -p /etc/nginx/ssl @@ -100,10 +106,12 @@ CookieAuthentication 1 EOF rm -rf /var/lib/tor/* -ln -sf /usr/lib/startos/scripts/tor-check.sh /usr/bin/tor-check -ln -sf /usr/lib/startos/scripts/gather_debug_info.sh /usr/bin/gather-debug +ln -sf /usr/lib/startos/scripts/chroot-and-upgrade /usr/bin/chroot-and-upgrade +ln -sf /usr/lib/startos/scripts/tor-check /usr/bin/tor-check +ln -sf /usr/lib/startos/scripts/gather-debug-info /usr/bin/gather-debug-info +ln -sf /usr/lib/startos/scripts/wg-vps-setup /usr/bin/wg-vps-setup -echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-embassy.conf +echo "fs.inotify.max_user_watches=1048576" > /etc/sysctl.d/97-startos.conf # Old pi was set with this locale, because of pg we are now stuck with including that locale locale-gen en_GB en_GB.UTF-8 @@ -112,16 +120,14 @@ update-locale LANGUAGE rm -f "/etc/locale.gen" dpkg-reconfigure --frontend noninteractive locales -if ! getent group | grep '^embassy:'; then - groupadd embassy +if ! getent group | grep '^startos:'; then + groupadd startos fi -ln -sf /usr/lib/startos/scripts/dhclient-exit-hook /etc/dhcp/dhclient-exit-hooks.d/embassy - rm -f /etc/motd -ln -sf /usr/lib/startos/motd /etc/update-motd.d/00-embassy +ln -sf /usr/lib/startos/motd /etc/update-motd.d/00-startos chmod -x /etc/update-motd.d/* -chmod +x /etc/update-motd.d/00-embassy +chmod +x /etc/update-motd.d/00-startos # LXC cat /etc/subuid | grep -v '^root:' > /etc/subuid.tmp || true diff --git a/image-recipe/build.sh b/image-recipe/build.sh index 4ce2d6dac..eaf5e8382 100755 --- a/image-recipe/build.sh +++ b/image-recipe/build.sh @@ -166,7 +166,7 @@ echo "deb [arch=${IB_TARGET_ARCH} signed-by=/etc/apt/trusted.gpg.d/docker.key.gp # Dependencies ## Base dependencies -dpkg-deb --fsys-tarfile $base_dir/deb/${IMAGE_BASENAME}.deb | tar --to-stdout -xvf - ./usr/lib/startos/depends > config/package-lists/embassy-depends.list.chroot +dpkg-deb --fsys-tarfile $base_dir/deb/${IMAGE_BASENAME}.deb | tar --to-stdout -xvf - ./usr/lib/startos/depends > config/package-lists/startos-depends.list.chroot ## Firmware if [ "$NON_FREE" = 1 ]; then @@ -206,11 +206,11 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then echo "Configuring raspi kernel '\$v'" extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v done - mkinitramfs -c gzip -o /boot/initramfs8 6.6.62-v8+ - mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.62-v8-16k+ + mkinitramfs -c gzip -o /boot/initramfs8 6.6.74-v8+ + mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.74-v8-16k+ fi -useradd --shell /bin/bash -G embassy -m start9 +useradd --shell /bin/bash -G startos -m start9 echo start9:embassy | chpasswd usermod -aG sudo start9 diff --git a/patch-db b/patch-db index 99076d349..0df18c651 160000 --- a/patch-db +++ b/patch-db @@ -1 +1 @@ -Subproject commit 99076d349c6768000483ea8d47216d273586552e +Subproject commit 0df18c651f2311e2e26f3e6c8535a9e40b71502f diff --git a/sdk/base/lib/Effects.ts b/sdk/base/lib/Effects.ts index e4424fafb..dcb03af4e 100644 --- a/sdk/base/lib/Effects.ts +++ b/sdk/base/lib/Effects.ts @@ -8,7 +8,7 @@ import { SetHealth, BindParams, HostId, - LanInfo, + NetInfo, Host, ExportServiceInterfaceParams, ServiceInterface, @@ -117,7 +117,7 @@ export type Effects = { packageId?: PackageId hostId: HostId internalPort: number - }): Promise + }): Promise /** Removes all network bindings, called in the setupInputSpec */ clearBindings(options: { except: { id: HostId; internalPort: number }[] @@ -129,12 +129,6 @@ export type Effects = { hostId: HostId callback?: () => void }): Promise - /** Returns the primary url that a user has selected for a host, if it exists */ - getPrimaryUrl(options: { - packageId?: PackageId - hostId: HostId - callback?: () => void - }): Promise /** Returns the IP address of the container */ getContainerIp(): Promise // interface diff --git a/sdk/base/lib/actions/input/builder/inputSpec.ts b/sdk/base/lib/actions/input/builder/inputSpec.ts index 31e06df4f..5d4d5c6bb 100644 --- a/sdk/base/lib/actions/input/builder/inputSpec.ts +++ b/sdk/base/lib/actions/input/builder/inputSpec.ts @@ -94,8 +94,8 @@ export class InputSpec, Store = never> { }, public validator: Parser, ) {} - _TYPE: Type = null as any as Type - _PARTIAL: DeepPartial = null as any as DeepPartial + public _TYPE: Type = null as any as Type + public _PARTIAL: DeepPartial = null as any as DeepPartial async build(options: LazyBuildOptions) { const answer = {} as { [K in keyof Type]: ValueSpec diff --git a/sdk/base/lib/actions/input/builder/value.ts b/sdk/base/lib/actions/input/builder/value.ts index 676c4aac1..3ff3c2d24 100644 --- a/sdk/base/lib/actions/input/builder/value.ts +++ b/sdk/base/lib/actions/input/builder/value.ts @@ -49,6 +49,9 @@ export class Value { public build: LazyBuild, public validator: Parser, ) {} + public _TYPE: Type = null as any as Type + public _PARTIAL: DeepPartial = null as any as DeepPartial + static toggle(a: { name: string description?: string | null diff --git a/sdk/base/lib/actions/input/inputSpecConstants.ts b/sdk/base/lib/actions/input/inputSpecConstants.ts index 57bf8a79b..64857d419 100644 --- a/sdk/base/lib/actions/input/inputSpecConstants.ts +++ b/sdk/base/lib/actions/input/inputSpecConstants.ts @@ -25,9 +25,9 @@ export const customSmtp = InputSpec.of, never>({ name: "From Address", required: true, default: null, - placeholder: "test@example.com", + placeholder: "Example Name ", inputmode: "email", - patterns: [Patterns.email], + patterns: [Patterns.emailWithName], }), login: Value.text({ name: "Login", diff --git a/sdk/base/lib/dependencies/setupDependencies.ts b/sdk/base/lib/dependencies/setupDependencies.ts index 6b15ef0d1..4583ae749 100644 --- a/sdk/base/lib/dependencies/setupDependencies.ts +++ b/sdk/base/lib/dependencies/setupDependencies.ts @@ -31,7 +31,7 @@ export type CurrentDependenciesResult = { [K in RequiredDependenciesOf]: DependencyRequirement } & { [K in OptionalDependenciesOf]?: DependencyRequirement -} & Record +} export function setupDependencies( fn: (options: { @@ -48,14 +48,16 @@ export function setupDependencies( } const dependencyType = await fn(options) return await options.effects.setDependencies({ - dependencies: Object.entries(dependencyType).map( - ([id, { versionRange, ...x }, ,]) => - ({ - // id, - ...x, - versionRange: versionRange.toString(), - }) as T.DependencyRequirement, - ), + dependencies: Object.entries(dependencyType) + .map(([k, v]) => [k, v as DependencyRequirement] as const) + .map( + ([id, { versionRange, ...x }]) => + ({ + id, + ...x, + versionRange: versionRange.toString(), + }) as T.DependencyRequirement, + ), }) } return cell.updater diff --git a/sdk/base/lib/interfaces/Host.ts b/sdk/base/lib/interfaces/Host.ts index b90dc1c60..53419357f 100644 --- a/sdk/base/lib/interfaces/Host.ts +++ b/sdk/base/lib/interfaces/Host.ts @@ -77,19 +77,18 @@ type BindOptionsByKnownProtocol = preferredExternalPort?: number addSsl?: AddSslOptions } -export type BindOptionsByProtocol = BindOptionsByKnownProtocol | BindOptions - -export type HostKind = BindParams["kind"] +export type BindOptionsByProtocol = + | BindOptionsByKnownProtocol + | (BindOptions & { protocol: null }) const hasStringProtocol = object({ protocol: string, }).test -export class Host { +export class MultiHost { constructor( readonly options: { effects: Effects - kind: HostKind id: string }, ) {} @@ -113,7 +112,7 @@ export class Host { async bindPort( internalPort: number, options: BindOptionsByProtocol, - ): Promise> { + ): Promise { if (hasStringProtocol(options)) { return await this.bindPortForKnown(options, internalPort) } else { @@ -130,7 +129,6 @@ export class Host { }, ) { const binderOptions = { - kind: this.options.kind, id: this.options.id, internalPort, ...options, @@ -163,7 +161,6 @@ export class Host { const secure: Security | null = !protoInfo.secure ? null : { ssl: false } await this.options.effects.bind({ - kind: this.options.kind, id: this.options.id, internalPort, preferredExternalPort, @@ -190,21 +187,3 @@ function inObject( ): obj is { [K in Key]: unknown } { return key in obj } - -// export class StaticHost extends Host { -// constructor(options: { effects: Effects; id: string }) { -// super({ ...options, kind: "static" }) -// } -// } - -// export class SingleHost extends Host { -// constructor(options: { effects: Effects; id: string }) { -// super({ ...options, kind: "single" }) -// } -// } - -export class MultiHost extends Host { - constructor(options: { effects: Effects; id: string }) { - super({ ...options, kind: "multi" }) - } -} diff --git a/sdk/base/lib/interfaces/Origin.ts b/sdk/base/lib/interfaces/Origin.ts index 5e12713e6..dd688b3c9 100644 --- a/sdk/base/lib/interfaces/Origin.ts +++ b/sdk/base/lib/interfaces/Origin.ts @@ -1,11 +1,11 @@ import { AddressInfo } from "../types" import { AddressReceipt } from "./AddressReceipt" -import { Host, Scheme } from "./Host" +import { MultiHost, Scheme } from "./Host" import { ServiceInterfaceBuilder } from "./ServiceInterfaceBuilder" -export class Origin { +export class Origin { constructor( - readonly host: T, + readonly host: MultiHost, readonly internalPort: number, readonly scheme: string | null, readonly sslScheme: string | null, @@ -46,7 +46,6 @@ export class Origin { const { name, description, - hasPrimary, id, type, username, @@ -67,7 +66,6 @@ export class Origin { id, name, description, - hasPrimary, addressInfo, type, masked, diff --git a/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts b/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts index 4ef294b4f..036180ad3 100644 --- a/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts +++ b/sdk/base/lib/interfaces/ServiceInterfaceBuilder.ts @@ -20,7 +20,6 @@ export class ServiceInterfaceBuilder { name: string id: string description: string - hasPrimary: boolean type: ServiceInterfaceType username: string | null path: string diff --git a/sdk/base/lib/osBindings/AcmeProvider.ts b/sdk/base/lib/osBindings/AcmeProvider.ts new file mode 100644 index 000000000..0ad3f0052 --- /dev/null +++ b/sdk/base/lib/osBindings/AcmeProvider.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AcmeProvider = string diff --git a/sdk/base/lib/osBindings/AcmeSettings.ts b/sdk/base/lib/osBindings/AcmeSettings.ts index bdf151ec7..44e70d9df 100644 --- a/sdk/base/lib/osBindings/AcmeSettings.ts +++ b/sdk/base/lib/osBindings/AcmeSettings.ts @@ -1,13 +1,3 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type AcmeSettings = { - provider: string - /** - * email addresses for letsencrypt - */ - contact: Array - /** - * domains to get letsencrypt certs for - */ - domains: string[] -} +export type AcmeSettings = { contact: Array } diff --git a/sdk/base/lib/osBindings/BindInfo.ts b/sdk/base/lib/osBindings/BindInfo.ts index 85fc38e94..b03dbe6b2 100644 --- a/sdk/base/lib/osBindings/BindInfo.ts +++ b/sdk/base/lib/osBindings/BindInfo.ts @@ -1,5 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { BindOptions } from "./BindOptions" -import type { LanInfo } from "./LanInfo" +import type { NetInfo } from "./NetInfo" -export type BindInfo = { enabled: boolean; options: BindOptions; lan: LanInfo } +export type BindInfo = { enabled: boolean; options: BindOptions; net: NetInfo } diff --git a/sdk/base/lib/osBindings/BindParams.ts b/sdk/base/lib/osBindings/BindParams.ts index fcc450476..9064a33b1 100644 --- a/sdk/base/lib/osBindings/BindParams.ts +++ b/sdk/base/lib/osBindings/BindParams.ts @@ -1,11 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AddSslOptions } from "./AddSslOptions" import type { HostId } from "./HostId" -import type { HostKind } from "./HostKind" import type { Security } from "./Security" export type BindParams = { - kind: HostKind id: HostId internalPort: number preferredExternalPort: number diff --git a/sdk/base/lib/osBindings/BindingSetPublicParams.ts b/sdk/base/lib/osBindings/BindingSetPublicParams.ts new file mode 100644 index 000000000..077cf8510 --- /dev/null +++ b/sdk/base/lib/osBindings/BindingSetPublicParams.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type BindingSetPublicParams = { + internalPort: number + public: boolean | null +} diff --git a/sdk/base/lib/osBindings/DomainConfig.ts b/sdk/base/lib/osBindings/DomainConfig.ts new file mode 100644 index 000000000..433bc65f5 --- /dev/null +++ b/sdk/base/lib/osBindings/DomainConfig.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 { AcmeProvider } from "./AcmeProvider" + +export type DomainConfig = { public: boolean; acme: AcmeProvider | null } diff --git a/sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts b/sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts index 28ac89916..675c3e06d 100644 --- a/sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts +++ b/sdk/base/lib/osBindings/ExportServiceInterfaceParams.ts @@ -7,7 +7,6 @@ export type ExportServiceInterfaceParams = { id: ServiceInterfaceId name: string description: string - hasPrimary: boolean masked: boolean addressInfo: AddressInfo type: ServiceInterfaceType diff --git a/sdk/base/lib/osBindings/ForgetInterfaceParams.ts b/sdk/base/lib/osBindings/ForgetInterfaceParams.ts new file mode 100644 index 000000000..b3532602c --- /dev/null +++ b/sdk/base/lib/osBindings/ForgetInterfaceParams.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ForgetInterfaceParams = { interface: string } diff --git a/sdk/base/lib/osBindings/GetPrimaryUrlParams.ts b/sdk/base/lib/osBindings/GetPrimaryUrlParams.ts deleted file mode 100644 index 06bf73976..000000000 --- a/sdk/base/lib/osBindings/GetPrimaryUrlParams.ts +++ /dev/null @@ -1,10 +0,0 @@ -// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { CallbackId } from "./CallbackId" -import type { HostId } from "./HostId" -import type { PackageId } from "./PackageId" - -export type GetPrimaryUrlParams = { - packageId?: PackageId - hostId: HostId - callback?: CallbackId -} diff --git a/sdk/base/lib/osBindings/HostKind.ts b/sdk/base/lib/osBindings/GitHash.ts similarity index 78% rename from sdk/base/lib/osBindings/HostKind.ts rename to sdk/base/lib/osBindings/GitHash.ts index 3c2f5ad87..43f6adde3 100644 --- a/sdk/base/lib/osBindings/HostKind.ts +++ b/sdk/base/lib/osBindings/GitHash.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 HostKind = "multi" +export type GitHash = string diff --git a/sdk/base/lib/osBindings/Host.ts b/sdk/base/lib/osBindings/Host.ts index 7d8cf3a90..041e1c9bc 100644 --- a/sdk/base/lib/osBindings/Host.ts +++ b/sdk/base/lib/osBindings/Host.ts @@ -1,13 +1,12 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { BindInfo } from "./BindInfo" -import type { HostAddress } from "./HostAddress" -import type { HostKind } from "./HostKind" +import type { DomainConfig } from "./DomainConfig" import type { HostnameInfo } from "./HostnameInfo" export type Host = { - kind: HostKind bindings: { [key: number]: BindInfo } - addresses: Array + onions: string[] + domains: { [key: string]: DomainConfig } /** * COMPUTED: NetService::update */ diff --git a/sdk/base/lib/osBindings/HostAddress.ts b/sdk/base/lib/osBindings/HostAddress.ts index 73b46d8e5..fe16c89d7 100644 --- a/sdk/base/lib/osBindings/HostAddress.ts +++ b/sdk/base/lib/osBindings/HostAddress.ts @@ -1,5 +1,11 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AcmeProvider } from "./AcmeProvider" export type HostAddress = | { kind: "onion"; address: string } - | { kind: "domain"; address: string } + | { + kind: "domain" + address: string + public: boolean + acme: AcmeProvider | null + } diff --git a/sdk/base/lib/osBindings/IpHostname.ts b/sdk/base/lib/osBindings/IpHostname.ts index 4a6b5e87c..9b3ddd6d1 100644 --- a/sdk/base/lib/osBindings/IpHostname.ts +++ b/sdk/base/lib/osBindings/IpHostname.ts @@ -2,7 +2,13 @@ export type IpHostname = | { kind: "ipv4"; value: string; port: number | null; sslPort: number | null } - | { kind: "ipv6"; value: string; port: number | null; sslPort: number | null } + | { + kind: "ipv6" + value: string + scopeId: number + port: number | null + sslPort: number | null + } | { kind: "local" value: string diff --git a/sdk/base/lib/osBindings/IpInfo.ts b/sdk/base/lib/osBindings/IpInfo.ts index ae8c88d1b..260add9e6 100644 --- a/sdk/base/lib/osBindings/IpInfo.ts +++ b/sdk/base/lib/osBindings/IpInfo.ts @@ -1,8 +1,10 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { NetworkInterfaceType } from "./NetworkInterfaceType" export type IpInfo = { - ipv4Range: string | null - ipv4: string | null - ipv6Range: string | null - ipv6: string | null + scopeId: number + deviceType: NetworkInterfaceType | null + subnets: string[] + wanIp: string | null + ntpServers: string[] } diff --git a/sdk/base/lib/osBindings/Manifest.ts b/sdk/base/lib/osBindings/Manifest.ts index 8007b565b..2c9a2457e 100644 --- a/sdk/base/lib/osBindings/Manifest.ts +++ b/sdk/base/lib/osBindings/Manifest.ts @@ -2,6 +2,7 @@ import type { Alerts } from "./Alerts" import type { Dependencies } from "./Dependencies" import type { Description } from "./Description" +import type { GitHash } from "./GitHash" import type { HardwareRequirements } from "./HardwareRequirements" import type { ImageConfig } from "./ImageConfig" import type { ImageId } from "./ImageId" @@ -30,6 +31,6 @@ export type Manifest = { alerts: Alerts dependencies: Dependencies hardwareRequirements: HardwareRequirements - gitHash: string | null + gitHash?: GitHash osVersion: string } diff --git a/sdk/base/lib/osBindings/LanInfo.ts b/sdk/base/lib/osBindings/NetInfo.ts similarity index 80% rename from sdk/base/lib/osBindings/LanInfo.ts rename to sdk/base/lib/osBindings/NetInfo.ts index 59b8a5519..e790cadaa 100644 --- a/sdk/base/lib/osBindings/LanInfo.ts +++ b/sdk/base/lib/osBindings/NetInfo.ts @@ -1,6 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type LanInfo = { +export type NetInfo = { + public: boolean assignedPort: number | null assignedSslPort: number | null } diff --git a/sdk/base/lib/osBindings/NetworkInterfaceInfo.ts b/sdk/base/lib/osBindings/NetworkInterfaceInfo.ts new file mode 100644 index 000000000..796046b93 --- /dev/null +++ b/sdk/base/lib/osBindings/NetworkInterfaceInfo.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { IpInfo } from "./IpInfo" + +export type NetworkInterfaceInfo = { + public: boolean | null + ipInfo: IpInfo | null +} diff --git a/sdk/base/lib/osBindings/NetworkInterfaceSetPublicParams.ts b/sdk/base/lib/osBindings/NetworkInterfaceSetPublicParams.ts new file mode 100644 index 000000000..516bfc817 --- /dev/null +++ b/sdk/base/lib/osBindings/NetworkInterfaceSetPublicParams.ts @@ -0,0 +1,6 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkInterfaceSetPublicParams = { + interface: string + public: boolean | null +} diff --git a/sdk/base/lib/osBindings/NetworkInterfaceType.ts b/sdk/base/lib/osBindings/NetworkInterfaceType.ts new file mode 100644 index 000000000..e20067dcc --- /dev/null +++ b/sdk/base/lib/osBindings/NetworkInterfaceType.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NetworkInterfaceType = "ethernet" | "wireless" | "wireguard" diff --git a/sdk/base/lib/osBindings/PackageVersionInfo.ts b/sdk/base/lib/osBindings/PackageVersionInfo.ts index 80481acb3..c71fd5921 100644 --- a/sdk/base/lib/osBindings/PackageVersionInfo.ts +++ b/sdk/base/lib/osBindings/PackageVersionInfo.ts @@ -3,6 +3,7 @@ import type { Alerts } from "./Alerts" import type { DataUrl } from "./DataUrl" import type { DependencyMetadata } from "./DependencyMetadata" import type { Description } from "./Description" +import type { GitHash } from "./GitHash" import type { HardwareRequirements } from "./HardwareRequirements" import type { MerkleArchiveCommitment } from "./MerkleArchiveCommitment" import type { PackageId } from "./PackageId" @@ -13,7 +14,7 @@ export type PackageVersionInfo = { icon: DataUrl description: Description releaseNotes: string - gitHash: string + gitHash: GitHash license: string wrapperRepo: string upstreamRepo: string diff --git a/sdk/base/lib/osBindings/ServerInfo.ts b/sdk/base/lib/osBindings/ServerInfo.ts index 89d7fc1b0..5779fa7d5 100644 --- a/sdk/base/lib/osBindings/ServerInfo.ts +++ b/sdk/base/lib/osBindings/ServerInfo.ts @@ -1,8 +1,10 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AcmeProvider } from "./AcmeProvider" import type { AcmeSettings } from "./AcmeSettings" import type { Governor } from "./Governor" -import type { IpInfo } from "./IpInfo" +import type { Host } from "./Host" import type { LshwDevice } from "./LshwDevice" +import type { NetworkInterfaceInfo } from "./NetworkInterfaceInfo" import type { ServerStatus } from "./ServerStatus" import type { SmtpValue } from "./SmtpValue" import type { WifiInfo } from "./WifiInfo" @@ -12,18 +14,13 @@ export type ServerInfo = { platform: string id: string hostname: string + host: Host version: string packageVersionCompat: string postInitMigrationTodos: string[] lastBackup: string | null - lanAddress: string - onionAddress: string - /** - * for backwards compatibility - */ - torAddress: string - ipInfo: { [key: string]: IpInfo } - acme: AcmeSettings | null + networkInterfaces: { [key: string]: NetworkInterfaceInfo } + acme: { [key: AcmeProvider]: AcmeSettings } statusInfo: ServerStatus wifi: WifiInfo unreadNotificationCount: number diff --git a/sdk/base/lib/osBindings/ServiceInterface.ts b/sdk/base/lib/osBindings/ServiceInterface.ts index 9bcec0056..6a58675a4 100644 --- a/sdk/base/lib/osBindings/ServiceInterface.ts +++ b/sdk/base/lib/osBindings/ServiceInterface.ts @@ -7,7 +7,6 @@ export type ServiceInterface = { id: ServiceInterfaceId name: string description: string - hasPrimary: boolean masked: boolean addressInfo: AddressInfo type: ServiceInterfaceType diff --git a/sdk/base/lib/osBindings/SetupResult.ts b/sdk/base/lib/osBindings/SetupResult.ts index d81c7f039..3147187c1 100644 --- a/sdk/base/lib/osBindings/SetupResult.ts +++ b/sdk/base/lib/osBindings/SetupResult.ts @@ -1,7 +1,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type SetupResult = { - torAddress: string + torAddresses: Array hostname: string lanAddress: string rootCa: string diff --git a/sdk/base/lib/osBindings/TestSmtpParams.ts b/sdk/base/lib/osBindings/TestSmtpParams.ts new file mode 100644 index 000000000..06b218a34 --- /dev/null +++ b/sdk/base/lib/osBindings/TestSmtpParams.ts @@ -0,0 +1,10 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TestSmtpParams = { + server: string + port: number + from: string + to: string + login: string + password: string | null +} diff --git a/sdk/base/lib/osBindings/UnsetPublicParams.ts b/sdk/base/lib/osBindings/UnsetPublicParams.ts new file mode 100644 index 000000000..db8f730e1 --- /dev/null +++ b/sdk/base/lib/osBindings/UnsetPublicParams.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type UnsetPublicParams = { interface: string } diff --git a/sdk/base/lib/osBindings/index.ts b/sdk/base/lib/osBindings/index.ts index f76f595c9..e2ab33033 100644 --- a/sdk/base/lib/osBindings/index.ts +++ b/sdk/base/lib/osBindings/index.ts @@ -1,4 +1,5 @@ export { AcceptSigners } from "./AcceptSigners" +export { AcmeProvider } from "./AcmeProvider" export { AcmeSettings } from "./AcmeSettings" export { ActionId } from "./ActionId" export { ActionInput } from "./ActionInput" @@ -37,6 +38,7 @@ export { BackupTargetFS } from "./BackupTargetFS" export { Base64 } from "./Base64" export { BindId } from "./BindId" export { BindInfo } from "./BindInfo" +export { BindingSetPublicParams } from "./BindingSetPublicParams" export { BindOptions } from "./BindOptions" export { BindParams } from "./BindParams" export { Blake3Commitment } from "./Blake3Commitment" @@ -66,6 +68,7 @@ export { DepInfo } from "./DepInfo" export { Description } from "./Description" export { DestroySubcontainerFsParams } from "./DestroySubcontainerFsParams" export { DeviceFilter } from "./DeviceFilter" +export { DomainConfig } from "./DomainConfig" export { Duration } from "./Duration" export { EchoParams } from "./EchoParams" export { EditSignerParams } from "./EditSignerParams" @@ -73,6 +76,7 @@ export { EncryptedWire } from "./EncryptedWire" export { ExportActionParams } from "./ExportActionParams" export { ExportServiceInterfaceParams } from "./ExportServiceInterfaceParams" export { ExposeForDependentsParams } from "./ExposeForDependentsParams" +export { ForgetInterfaceParams } from "./ForgetInterfaceParams" export { FullIndex } from "./FullIndex" export { FullProgress } from "./FullProgress" export { GetActionInputParams } from "./GetActionInputParams" @@ -82,7 +86,6 @@ export { GetOsVersionParams } from "./GetOsVersionParams" export { GetPackageParams } from "./GetPackageParams" export { GetPackageResponseFull } from "./GetPackageResponseFull" export { GetPackageResponse } from "./GetPackageResponse" -export { GetPrimaryUrlParams } from "./GetPrimaryUrlParams" export { GetServiceInterfaceParams } from "./GetServiceInterfaceParams" export { GetServicePortForwardParams } from "./GetServicePortForwardParams" export { GetSslCertificateParams } from "./GetSslCertificateParams" @@ -90,13 +93,13 @@ export { GetSslKeyParams } from "./GetSslKeyParams" export { GetStatusParams } from "./GetStatusParams" export { GetStoreParams } from "./GetStoreParams" export { GetSystemSmtpParams } from "./GetSystemSmtpParams" +export { GitHash } from "./GitHash" export { Governor } from "./Governor" export { Guid } from "./Guid" export { HardwareRequirements } from "./HardwareRequirements" export { HealthCheckId } from "./HealthCheckId" export { HostAddress } from "./HostAddress" export { HostId } from "./HostId" -export { HostKind } from "./HostKind" export { HostnameInfo } from "./HostnameInfo" export { Hosts } from "./Hosts" export { Host } from "./Host" @@ -112,7 +115,6 @@ export { InstallingState } from "./InstallingState" export { InstallParams } from "./InstallParams" export { IpHostname } from "./IpHostname" export { IpInfo } from "./IpInfo" -export { LanInfo } from "./LanInfo" export { ListPackageSignersParams } from "./ListPackageSignersParams" export { ListServiceInterfacesParams } from "./ListServiceInterfacesParams" export { ListVersionSignersParams } from "./ListVersionSignersParams" @@ -128,6 +130,10 @@ export { MountParams } from "./MountParams" export { MountTarget } from "./MountTarget" export { NamedHealthCheckResult } from "./NamedHealthCheckResult" export { NamedProgress } from "./NamedProgress" +export { NetInfo } from "./NetInfo" +export { NetworkInterfaceInfo } from "./NetworkInterfaceInfo" +export { NetworkInterfaceSetPublicParams } from "./NetworkInterfaceSetPublicParams" +export { NetworkInterfaceType } from "./NetworkInterfaceType" export { OnionHostname } from "./OnionHostname" export { OsIndex } from "./OsIndex" export { OsVersionInfoMap } from "./OsVersionInfoMap" @@ -181,6 +187,8 @@ export { SignAssetParams } from "./SignAssetParams" export { SignerInfo } from "./SignerInfo" export { SmtpValue } from "./SmtpValue" export { StartStop } from "./StartStop" +export { TestSmtpParams } from "./TestSmtpParams" +export { UnsetPublicParams } from "./UnsetPublicParams" export { UpdatingState } from "./UpdatingState" export { VerifyCifsParams } from "./VerifyCifsParams" export { VersionSignerParams } from "./VersionSignerParams" diff --git a/sdk/base/lib/test/startosTypeValidation.test.ts b/sdk/base/lib/test/startosTypeValidation.test.ts index 509da0894..2de7b43a4 100644 --- a/sdk/base/lib/test/startosTypeValidation.test.ts +++ b/sdk/base/lib/test/startosTypeValidation.test.ts @@ -26,7 +26,6 @@ import { SetDependenciesParams } from ".././osBindings" import { GetSystemSmtpParams } from ".././osBindings" import { GetServicePortForwardParams } from ".././osBindings" import { ExportServiceInterfaceParams } from ".././osBindings" -import { GetPrimaryUrlParams } from ".././osBindings" import { ListServiceInterfacesParams } from ".././osBindings" import { ExportActionParams } from ".././osBindings" import { MountParams } from ".././osBindings" @@ -83,7 +82,6 @@ describe("startosTypeValidation ", () => { getServicePortForward: {} as GetServicePortForwardParams, clearServiceInterfaces: {} as ClearServiceInterfacesParams, exportServiceInterface: {} as ExportServiceInterfaceParams, - getPrimaryUrl: {} as WithCallback, listServiceInterfaces: {} as WithCallback, mount: {} as MountParams, checkDependencies: {} as CheckDependenciesParam, diff --git a/sdk/base/lib/types.ts b/sdk/base/lib/types.ts index 36d4bd293..85a8c4404 100644 --- a/sdk/base/lib/types.ts +++ b/sdk/base/lib/types.ts @@ -138,33 +138,6 @@ export declare const hostName: unique symbol // asdflkjadsf.onion | 1.2.3.4 export type Hostname = string & { [hostName]: never } -export type HostnameInfoIp = { - kind: "ip" - networkInterfaceId: string - public: boolean - hostname: - | { - kind: "ipv4" | "ipv6" | "local" - value: string - port: number | null - sslPort: number | null - } - | { - kind: "domain" - domain: string - subdomain: string | null - port: number | null - sslPort: number | null - } -} - -export type HostnameInfoOnion = { - kind: "onion" - hostname: { value: string; port: number | null; sslPort: number | null } -} - -export type HostnameInfo = HostnameInfoIp | HostnameInfoOnion - export type ServiceInterfaceId = string export { ServiceInterface } diff --git a/sdk/base/lib/util/getServiceInterface.ts b/sdk/base/lib/util/getServiceInterface.ts index cbbb345cb..2e81e5ee2 100644 --- a/sdk/base/lib/util/getServiceInterface.ts +++ b/sdk/base/lib/util/getServiceInterface.ts @@ -1,15 +1,6 @@ import { ServiceInterfaceType } from "../types" import { knownProtocols } from "../interfaces/Host" -import { - AddressInfo, - Host, - HostAddress, - Hostname, - HostnameInfo, - HostnameInfoIp, - HostnameInfoOnion, - IpInfo, -} from "../types" +import { AddressInfo, Host, Hostname, HostnameInfo } from "../types" import { Effects } from "../Effects" export type UrlString = string @@ -48,8 +39,6 @@ export type ServiceInterfaceFilled = { name: string /** Human readable description, used as tooltip usually */ description: string - /** Whether or not the interface has a primary URL */ - hasPrimary: boolean /** Whether or not to mask the URIs for this interface. Useful if the URIs contain sensitive information, such as a password, macaroon, or API key */ masked: boolean /** Information about the host for this binding */ @@ -58,10 +47,6 @@ export type ServiceInterfaceFilled = { addressInfo: FilledAddressInfo | null /** Indicates if we are a ui/p2p/api for the kind of interface that this is representing */ type: ServiceInterfaceType - /** The primary hostname for the service, as chosen by the user */ - primaryHostname: Hostname | null - /** The primary URL for the service, as chosen by the user */ - primaryUrl: UrlString | null } const either = (...args: ((a: A) => boolean)[]) => @@ -89,7 +74,9 @@ export const addressHostToUrl = ( if (host.hostname.kind === "domain") { hostname = `${host.hostname.subdomain ? `${host.hostname.subdomain}.` : ""}${host.hostname.domain}` } else if (host.hostname.kind === "ipv6") { - hostname = `[${host.hostname.value}]` + hostname = host.hostname.value.startsWith("fe80::") + ? `[${host.hostname.value}%${host.hostname.scopeId}]` + : `[${host.hostname.value}]` } else { hostname = host.hostname.value } @@ -200,23 +187,13 @@ const makeInterfaceFilled = async ({ hostId, callback, }) - const primaryUrl = await effects.getPrimaryUrl({ - hostId, - packageId, - callback, - }) const interfaceFilled: ServiceInterfaceFilled = { ...serviceInterfaceValue, - primaryUrl: primaryUrl, host, addressInfo: host ? filledAddress(host, serviceInterfaceValue.addressInfo) : null, - get primaryHostname() { - if (primaryUrl == null) return null - return getHostname(primaryUrl) - }, } return interfaceFilled } diff --git a/sdk/base/lib/util/getServiceInterfaces.ts b/sdk/base/lib/util/getServiceInterfaces.ts index 1d83684d6..faeb508b4 100644 --- a/sdk/base/lib/util/getServiceInterfaces.ts +++ b/sdk/base/lib/util/getServiceInterfaces.ts @@ -30,22 +30,10 @@ const makeManyInterfaceFilled = async ({ if (!host) { throw new Error(`host ${hostId} not found!`) } - const primaryUrl = await effects - .getPrimaryUrl({ - hostId, - packageId, - callback, - }) - .catch(() => null) return { ...serviceInterfaceValue, - primaryUrl: primaryUrl, host, addressInfo: filledAddress(host, serviceInterfaceValue.addressInfo), - get primaryHostname() { - if (primaryUrl == null) return null - return getHostname(primaryUrl) - }, } }), ) diff --git a/sdk/base/lib/util/patterns.ts b/sdk/base/lib/util/patterns.ts index 2c9c7010d..a61e4269c 100644 --- a/sdk/base/lib/util/patterns.ts +++ b/sdk/base/lib/util/patterns.ts @@ -2,58 +2,68 @@ import { Pattern } from "../actions/input/inputSpecTypes" import * as regexes from "./regexes" export const ipv6: Pattern = { - regex: regexes.ipv6.toString(), + regex: regexes.ipv6.matches(), description: "Must be a valid IPv6 address", } export const ipv4: Pattern = { - regex: regexes.ipv4.toString(), + regex: regexes.ipv4.matches(), description: "Must be a valid IPv4 address", } export const hostname: Pattern = { - regex: regexes.hostname.toString(), + regex: regexes.hostname.matches(), description: "Must be a valid hostname", } export const localHostname: Pattern = { - regex: regexes.localHostname.toString(), + regex: regexes.localHostname.matches(), description: 'Must be a valid ".local" hostname', } export const torHostname: Pattern = { - regex: regexes.torHostname.toString(), + regex: regexes.torHostname.matches(), description: 'Must be a valid Tor (".onion") hostname', } export const url: Pattern = { - regex: regexes.url.toString(), + regex: regexes.url.matches(), description: "Must be a valid URL", } export const localUrl: Pattern = { - regex: regexes.localUrl.toString(), + regex: regexes.localUrl.matches(), description: 'Must be a valid ".local" URL', } export const torUrl: Pattern = { - regex: regexes.torUrl.toString(), + regex: regexes.torUrl.matches(), description: 'Must be a valid Tor (".onion") URL', } export const ascii: Pattern = { - regex: regexes.ascii.toString(), + regex: regexes.ascii.matches(), description: "May only contain ASCII characters. See https://www.w3schools.com/charsets/ref_html_ascii.asp", } +export const domain: Pattern = { + regex: regexes.domain.matches(), + description: "Must be a valid Fully Qualified Domain Name", +} + export const email: Pattern = { - regex: regexes.email.toString(), + regex: regexes.email.matches(), description: "Must be a valid email address", } +export const emailWithName: Pattern = { + regex: regexes.emailWithName.matches(), + description: "Must be a valid email address, optionally with a name", +} + export const base64: Pattern = { - regex: regexes.base64.toString(), + regex: regexes.base64.matches(), description: "May only contain base64 characters. See https://base64.guru/learn/base64-characters", } diff --git a/sdk/base/lib/util/regexes.ts b/sdk/base/lib/util/regexes.ts index f26196381..a2a8cde7a 100644 --- a/sdk/base/lib/util/regexes.ts +++ b/sdk/base/lib/util/regexes.ts @@ -1,34 +1,71 @@ +export class ComposableRegex { + readonly regex: RegExp + constructor(regex: RegExp | string) { + if (regex instanceof RegExp) { + this.regex = regex + } else { + this.regex = new RegExp(regex) + } + } + asExpr(): string { + return `(${this.regex.source})` + } + matches(): string { + return `^${this.regex.source}$` + } + contains(): string { + return this.regex.source + } +} + // https://ihateregex.io/expr/ipv6/ -export const ipv6 = - /(([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]))/ +export const ipv6 = new ComposableRegex( + /(([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]))/, +) // https://ihateregex.io/expr/ipv4/ -export const ipv4 = - /(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/ +export const ipv4 = new ComposableRegex( + /(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/, +) -export const hostname = - /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/ +export const hostname = new ComposableRegex( + /(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])/, +) -export const localHostname = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local/ +export const localHostname = new ComposableRegex( + /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local/, +) -export const torHostname = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion/ +export const torHostname = new ComposableRegex( + /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion/, +) // https://ihateregex.io/expr/url/ -export const url = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const url = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) -export const localUrl = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const localUrl = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.local\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) -export const torUrl = - /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/ +export const torUrl = new ComposableRegex( + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.onion\b([-a-zA-Z0-9()!@:%_\+.~#?&\/\/=]*)/, +) // https://ihateregex.io/expr/ascii/ -export const ascii = /^[ -~]*$/ +export const ascii = new ComposableRegex(/[ -~]*/) -//https://ihateregex.io/expr/email/ -export const email = /[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/ +export const domain = new ComposableRegex(/[A-Za-z0-9.-]+\.[A-Za-z]{2,}/) + +// https://www.regular-expressions.info/email.html +export const email = new ComposableRegex(`[A-Za-z0-9._%+-]+@${domain.asExpr()}`) + +export const emailWithName = new ComposableRegex( + `${email.asExpr()}|([^<]*<${email.asExpr()}>)`, +) //https://rgxdb.com/r/1NUN74O6 -export const base64 = - /^(?:[a-zA-Z0-9+\/]{4})*(?:|(?:[a-zA-Z0-9+\/]{3}=)|(?:[a-zA-Z0-9+\/]{2}==)|(?:[a-zA-Z0-9+\/]{1}===))$/ +export const base64 = new ComposableRegex( + /(?:[a-zA-Z0-9+\/]{4})*(?:|(?:[a-zA-Z0-9+\/]{3}=)|(?:[a-zA-Z0-9+\/]{2}==)|(?:[a-zA-Z0-9+\/]{1}===))/, +) diff --git a/sdk/base/package-lock.json b/sdk/base/package-lock.json index ffcf8b85d..428dc6ac9 100644 --- a/sdk/base/package-lock.json +++ b/sdk/base/package-lock.json @@ -13,7 +13,7 @@ "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", "mime-types": "^2.1.35", - "ts-matches": "^6.1.0", + "ts-matches": "^6.2.1", "yaml": "^2.2.2" }, "devDependencies": { diff --git a/sdk/base/package.json b/sdk/base/package.json index 4cc2fc7ca..6eae719a7 100644 --- a/sdk/base/package.json +++ b/sdk/base/package.json @@ -27,7 +27,7 @@ "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", "mime-types": "^2.1.35", - "ts-matches": "^6.1.0", + "ts-matches": "^6.2.1", "yaml": "^2.2.2" }, "prettier": { diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index e7e87f963..667e4297b 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -102,7 +102,6 @@ export class StartSdk { | "clearServiceInterfaces" | "bind" | "getHostInfo" - | "getPrimaryUrl" type MainUsedEffects = "setMainStatus" | "setHealth" type CallbackEffects = "constRetry" | "clearCallbacks" type AlreadyExposed = "getSslCertificate" | "getSystemSmtp" @@ -216,18 +215,14 @@ export class StartSdk { }), }, - host: { - // static: (effects: Effects, id: string) => - // new StaticHost({ id, effects }), - // single: (effects: Effects, id: string) => - // new SingleHost({ id, effects }), - multi: (effects: Effects, id: string) => new MultiHost({ id, effects }), + MultiHost: { + of: (effects: Effects, id: string) => new MultiHost({ id, effects }), }, nullIfEmpty, runCommand: async ( effects: Effects, image: { - id: keyof Manifest["images"] & T.ImageId + imageId: keyof Manifest["images"] & T.ImageId sharedRun?: boolean }, command: T.CommandType, @@ -379,7 +374,6 @@ export class StartSdk { id: 'ui', description: 'The primary web app for this service.', type: 'ui', - hasPrimary: false, masked: false, schemeOverride: null, username: null, @@ -397,8 +391,6 @@ export class StartSdk { id: string /** The human readable description. */ description: string - /** No effect until StartOS v0.4.0. If true, forces the user to select one URL (i.e. .onion, .local, or IP address) as the primary URL. This is needed by some services to function properly. */ - hasPrimary: boolean /** Affects how the interface appears to the user. One of: 'ui', 'api', 'p2p'. If 'ui', the user will see a "Launch UI" button */ type: ServiceInterfaceType /** (optional) prepends the provided username to all URLs. */ @@ -552,7 +544,7 @@ export class StartSdk { inputSpecSpec, async ({ effects, input }) => { // ** UI multi-host ** - const uiMulti = sdk.host.multi(effects, 'ui-multi') + const uiMulti = sdk.MultiHost.of(effects, 'ui-multi') const uiMultiOrigin = await uiMulti.bindPort(80, { protocol: 'http', }) @@ -562,7 +554,6 @@ export class StartSdk { id: 'primary-ui', description: 'The primary web app for this service.', type: 'ui', - hasPrimary: false, masked: false, schemeOverride: null, username: null, @@ -575,7 +566,6 @@ export class StartSdk { id: 'admin-ui', description: 'The admin web app for this service.', type: 'ui', - hasPrimary: false, masked: false, schemeOverride: null, username: null, @@ -586,7 +576,7 @@ export class StartSdk { const uiReceipt = await uiMultiOrigin.export([primaryUi, adminUi]) // ** API multi-host ** - const apiMulti = sdk.host.multi(effects, 'api-multi') + const apiMulti = sdk.MultiHost.of(effects, 'api-multi') const apiMultiOrigin = await apiMulti.bindPort(5959, { protocol: 'http', }) @@ -596,7 +586,6 @@ export class StartSdk { id: 'api', description: 'The advanced API for this service.', type: 'api', - hasPrimary: false, masked: false, schemeOverride: null, username: null, @@ -688,6 +677,18 @@ export class StartSdk { return Daemons.of({ effects, started, healthReceipts }) }, }, + SubContainer: { + of( + effects: Effects, + image: { + imageId: T.ImageId & keyof Manifest["images"] + sharedRun?: boolean + }, + name: string, + ) { + return SubContainer.of(effects, image, name) + }, + }, List: { /** * @description Create a list of text inputs. @@ -1269,7 +1270,6 @@ export class StartSdk { * @example default: 'radio1' */ default: keyof Variants & string - required: boolean /** * @description A mapping of unique radio options to their human readable display format. * @example @@ -1410,7 +1410,7 @@ export class StartSdk { export async function runCommand( effects: Effects, - image: { id: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, + image: { imageId: keyof Manifest["images"] & T.ImageId; sharedRun?: boolean }, command: string | [string, ...string[]], options: CommandOptions & { mounts?: { path: string; options: MountOptions }[] diff --git a/sdk/package/lib/health/checkFns/checkPortListening.ts b/sdk/package/lib/health/checkFns/checkPortListening.ts index 0d6792b86..59cd9717f 100644 --- a/sdk/package/lib/health/checkFns/checkPortListening.ts +++ b/sdk/package/lib/health/checkFns/checkPortListening.ts @@ -6,16 +6,17 @@ import * as CP from "node:child_process" const cpExec = promisify(CP.exec) -export function containsAddress(x: string, port: number) { +export function containsAddress(x: string, port: number, address?: bigint) { const readPorts = x .split("\n") .filter(Boolean) .splice(1) - .map((x) => x.split(" ").filter(Boolean)[1]?.split(":")?.[1]) - .filter(Boolean) - .map((x) => Number.parseInt(x, 16)) - .filter(Number.isFinite) - return readPorts.indexOf(port) >= 0 + .map((x) => x.split(" ").filter(Boolean)[1]?.split(":")) + .filter((x) => x?.length > 1) + .map(([addr, p]) => [BigInt(`0x${addr}`), Number.parseInt(p, 16)] as const) + return !!readPorts.find( + ([addr, p]) => (address === undefined || address === addr) && port === p, + ) } /** @@ -39,9 +40,19 @@ export async function checkPortListening( await cpExec(`cat /proc/net/tcp`, {}).then(stringFromStdErrOut), port, ) || + containsAddress( + await cpExec(`cat /proc/net/tcp6`, {}).then(stringFromStdErrOut), + port, + BigInt(0), + ) || containsAddress( await cpExec("cat /proc/net/udp", {}).then(stringFromStdErrOut), port, + ) || + containsAddress( + await cpExec("cat /proc/net/udp6", {}).then(stringFromStdErrOut), + port, + BigInt(0), ) if (hasAddress) { return { result: "success", message: options.successMessage } diff --git a/sdk/package/lib/mainFn/CommandController.ts b/sdk/package/lib/mainFn/CommandController.ts index 3b2285adb..a7375b369 100644 --- a/sdk/package/lib/mainFn/CommandController.ts +++ b/sdk/package/lib/mainFn/CommandController.ts @@ -23,7 +23,7 @@ export class CommandController { effects: T.Effects, subcontainer: | { - id: keyof Manifest["images"] & T.ImageId + imageId: keyof Manifest["images"] & T.ImageId sharedRun?: boolean } | SubContainer, @@ -60,51 +60,59 @@ export class CommandController { } return subc })() - let childProcess: cp.ChildProcess - if (options.runAsInit) { - childProcess = await subc.launch(commands, { - env: options.env, - }) - } else { - childProcess = await subc.spawn(commands, { - env: options.env, - stdio: options.onStdout || options.onStderr ? "pipe" : "inherit", + + try { + let childProcess: cp.ChildProcess + if (options.runAsInit) { + childProcess = await subc.launch(commands, { + env: options.env, + }) + } else { + childProcess = await subc.spawn(commands, { + env: options.env, + stdio: options.onStdout || options.onStderr ? "pipe" : "inherit", + }) + } + + if (options.onStdout) childProcess.stdout?.on("data", options.onStdout) + if (options.onStderr) childProcess.stderr?.on("data", options.onStderr) + + const state = { exited: false } + const answer = new Promise((resolve, reject) => { + childProcess.on("exit", (code) => { + state.exited = true + if ( + code === 0 || + code === 143 || + (code === null && childProcess.signalCode == "SIGTERM") + ) { + return resolve(null) + } + if (code) { + return reject( + new Error(`${commands[0]} exited with code ${code}`), + ) + } else { + return reject( + new Error( + `${commands[0]} exited with signal ${childProcess.signalCode}`, + ), + ) + } + }) }) + + return new CommandController( + answer, + state, + subc, + childProcess, + options.sigtermTimeout, + ) + } catch (e) { + await subc.destroy() + throw e } - - if (options.onStdout) childProcess.stdout?.on("data", options.onStdout) - if (options.onStderr) childProcess.stderr?.on("data", options.onStderr) - - const state = { exited: false } - const answer = new Promise((resolve, reject) => { - childProcess.on("exit", (code) => { - state.exited = true - if ( - code === 0 || - code === 143 || - (code === null && childProcess.signalCode == "SIGTERM") - ) { - return resolve(null) - } - if (code) { - return reject(new Error(`${commands[0]} exited with code ${code}`)) - } else { - return reject( - new Error( - `${commands[0]} exited with signal ${childProcess.signalCode}`, - ), - ) - } - }) - }) - - return new CommandController( - answer, - state, - subc, - childProcess, - options.sigtermTimeout, - ) } } get subContainerHandle() { @@ -121,7 +129,7 @@ export class CommandController { if (!this.state.exited) { this.process.kill("SIGKILL") } - await this.subcontainer.destroy?.().catch((_) => {}) + await this.subcontainer.destroy().catch((_) => {}) } } async term({ signal = SIGTERM, timeout = this.sigtermTimeout } = {}) { @@ -141,7 +149,7 @@ export class CommandController { await this.runningAnswer } finally { - await this.subcontainer.destroy?.() + await this.subcontainer.destroy() } } } diff --git a/sdk/package/lib/mainFn/Daemon.ts b/sdk/package/lib/mainFn/Daemon.ts index d9db89b6a..864ae4122 100644 --- a/sdk/package/lib/mainFn/Daemon.ts +++ b/sdk/package/lib/mainFn/Daemon.ts @@ -22,7 +22,7 @@ export class Daemon { effects: T.Effects, subcontainer: | { - id: keyof Manifest["images"] & T.ImageId + imageId: keyof Manifest["images"] & T.ImageId sharedRun?: boolean } | SubContainer, @@ -60,6 +60,8 @@ export class Daemon { let timeoutCounter = 0 new Promise(async () => { while (this.shouldBeRunning) { + if (this.commandController) + await this.commandController.term().catch((err) => console.error(err)) this.commandController = await this.startCommand() await this.commandController.wait().catch((err) => console.error(err)) await new Promise((resolve) => setTimeout(resolve, timeoutCounter)) diff --git a/sdk/package/lib/mainFn/Daemons.ts b/sdk/package/lib/mainFn/Daemons.ts index 495e6c527..8d0e6297a 100644 --- a/sdk/package/lib/mainFn/Daemons.ts +++ b/sdk/package/lib/mainFn/Daemons.ts @@ -5,7 +5,7 @@ import { HealthCheckResult } from "../health/checkFns" import { Trigger } from "../trigger" import * as T from "../../../base/lib/types" import { Mounts } from "./Mounts" -import { ExecSpawnable, MountOptions } from "../util/SubContainer" +import { ExecSpawnable, MountOptions, SubContainer } from "../util/SubContainer" import { promisify } from "node:util" import * as CP from "node:child_process" @@ -49,16 +49,18 @@ type DaemonsParams< > = { /** The command line command to start the daemon */ command: T.CommandType - /** Information about the image in which the daemon runs */ - image: { - /** The ID of the image. Must be one of the image IDs declared in the manifest */ - id: keyof Manifest["images"] & T.ImageId - /** - * Whether or not to share the `/run` directory with the parent container. - * This is useful if you are trying to connect to a service that exposes a unix domain socket or auth cookie via the `/run` directory - */ - sharedRun?: boolean - } + /** Information about the subcontainer in which the daemon runs */ + subcontainer: + | { + /** The ID of the image. Must be one of the image IDs declared in the manifest */ + imageId: keyof Manifest["images"] & T.ImageId + /** + * Whether or not to share the `/run` directory with the parent container. + * This is useful if you are trying to connect to a service that exposes a unix domain socket or auth cookie via the `/run` directory + */ + sharedRun?: boolean + } + | SubContainer /** For mounting the necessary volumes. Syntax: sdk.Mounts.of().addVolume() */ mounts: Mounts env?: Record @@ -147,11 +149,16 @@ export class Daemons options: DaemonsParams, ) { const daemonIndex = this.daemons.length - const daemon = Daemon.of()(this.effects, options.image, options.command, { - ...options, - mounts: options.mounts.build(), - subcontainerName: id, - }) + const daemon = Daemon.of()( + this.effects, + options.subcontainer, + options.command, + { + ...options, + mounts: options.mounts.build(), + subcontainerName: id, + }, + ) const healthDaemon = new HealthDaemon( daemon, daemonIndex, @@ -178,14 +185,18 @@ export class Daemons } async build() { - this.updateMainHealth() - this.healthDaemons.forEach((x) => - x.addWatcher(() => this.updateMainHealth()), - ) const built = { - term: async (options?: { signal?: Signals; timeout?: number }) => { + term: async () => { try { - await Promise.all(this.healthDaemons.map((x) => x.term(options))) + for (let result of await Promise.allSettled( + this.healthDaemons.map((x) => + x.term({ timeout: x.sigtermTimeout }), + ), + )) { + if (result.status === "rejected") { + console.error(result.reason) + } + } } finally { this.effects.setMainStatus({ status: "stopped" }) } @@ -194,8 +205,4 @@ export class Daemons this.started(() => built.term()) return built } - - private updateMainHealth() { - this.effects.setMainStatus({ status: "running" }) - } } diff --git a/sdk/package/lib/mainFn/HealthDaemon.ts b/sdk/package/lib/mainFn/HealthDaemon.ts index b66e3e406..ac459f08c 100644 --- a/sdk/package/lib/mainFn/HealthDaemon.ts +++ b/sdk/package/lib/mainFn/HealthDaemon.ts @@ -25,6 +25,8 @@ export class HealthDaemon { private _health: HealthCheckResult = { result: "starting", message: null } private healthWatchers: Array<() => unknown> = [] private running = false + private resolveReady: (() => void) | undefined + private readyPromise: Promise constructor( private readonly daemon: Promise, readonly daemonIndex: number, @@ -35,6 +37,7 @@ export class HealthDaemon { readonly effects: Effects, readonly sigtermTimeout: number = DEFAULT_SIGTERM_TIMEOUT, ) { + this.readyPromise = new Promise((resolve) => (this.resolveReady = resolve)) this.updateStatus() this.dependencies.forEach((d) => d.addWatcher(() => this.updateStatus())) } @@ -112,6 +115,12 @@ export class HealthDaemon { message: "message" in err ? err.message : String(err), } }) + if ( + this.resolveReady && + (response.result === "success" || response.result === "disabled") + ) { + this.resolveReady() + } await this.setHealth(response) } else { await this.setHealth({ @@ -129,6 +138,10 @@ export class HealthDaemon { } } + onReady() { + return this.readyPromise + } + private async setHealth(health: HealthCheckResult) { this._health = health this.healthWatchers.forEach((watcher) => watcher()) diff --git a/sdk/package/lib/manifest/setupManifest.ts b/sdk/package/lib/manifest/setupManifest.ts index 1a78c062c..3cd4f8bfb 100644 --- a/sdk/package/lib/manifest/setupManifest.ts +++ b/sdk/package/lib/manifest/setupManifest.ts @@ -26,16 +26,6 @@ export function setupManifest< return manifest } -function gitHash(): string { - const hash = execSync("git rev-parse HEAD").toString().trim() - try { - execSync("git diff-index --quiet HEAD --") - return hash - } catch (e) { - return hash + "-modified" - } -} - export function buildManifest< Id extends string, Version extends string, @@ -68,7 +58,6 @@ export function buildManifest< ) return { ...manifest, - gitHash: gitHash(), osVersion: SDKVersion, version: versions.current.options.version, releaseNotes: versions.current.options.releaseNotes, diff --git a/sdk/package/lib/test/host.test.ts b/sdk/package/lib/test/host.test.ts index 87f22b8bd..88ca7c4b6 100644 --- a/sdk/package/lib/test/host.test.ts +++ b/sdk/package/lib/test/host.test.ts @@ -5,7 +5,7 @@ import { sdk } from "../test/output.sdk" describe("host", () => { test("Testing that the types work", () => { async function test(effects: Effects) { - const foo = sdk.host.multi(effects, "foo") + const foo = sdk.MultiHost.of(effects, "foo") const fooOrigin = await foo.bindPort(80, { protocol: "http" as const, preferredExternalPort: 80, @@ -15,7 +15,6 @@ describe("host", () => { name: "Foo", id: "foo", description: "A Foo", - hasPrimary: false, type: "ui", username: "bar", path: "/baz", diff --git a/sdk/package/lib/util/SubContainer.ts b/sdk/package/lib/util/SubContainer.ts index f9b5a1084..77b31fec8 100644 --- a/sdk/package/lib/util/SubContainer.ts +++ b/sdk/package/lib/util/SubContainer.ts @@ -86,12 +86,12 @@ export class SubContainer implements ExecSpawnable { } static async of( effects: T.Effects, - image: { id: T.ImageId; sharedRun?: boolean }, + image: { imageId: T.ImageId; sharedRun?: boolean }, name: string, ) { - const { id, sharedRun } = image + const { imageId, sharedRun } = image const [rootfs, guid] = await effects.subcontainer.createFs({ - imageId: id as string, + imageId, name, }) @@ -111,12 +111,12 @@ export class SubContainer implements ExecSpawnable { await execFile("mount", ["--rbind", from, to]) } - return new SubContainer(effects, id, rootfs, guid) + return new SubContainer(effects, imageId, rootfs, guid) } static async with( effects: T.Effects, - image: { id: T.ImageId; sharedRun?: boolean }, + image: { imageId: T.ImageId; sharedRun?: boolean }, mounts: { options: MountOptions; path: string }[], name: string, fn: (subContainer: SubContainer) => Promise, diff --git a/sdk/package/package-lock.json b/sdk/package/package-lock.json index ae387e4fd..b9620fd32 100644 --- a/sdk/package/package-lock.json +++ b/sdk/package/package-lock.json @@ -1,12 +1,12 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha.21", + "version": "0.3.6-beta.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha.21", + "version": "0.3.6-beta.4", "license": "MIT", "dependencies": { "@iarna/toml": "^2.2.5", @@ -15,7 +15,7 @@ "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", "mime-types": "^2.1.35", - "ts-matches": "^6.1.0", + "ts-matches": "^6.2.1", "yaml": "^2.2.2" }, "devDependencies": { diff --git a/sdk/package/package.json b/sdk/package/package.json index 7e97d4a32..53997d979 100644 --- a/sdk/package/package.json +++ b/sdk/package/package.json @@ -1,6 +1,6 @@ { "name": "@start9labs/start-sdk", - "version": "0.3.6-alpha.21", + "version": "0.3.6-beta.4", "description": "Software development kit to facilitate packaging services for StartOS", "main": "./package/lib/index.js", "types": "./package/lib/index.d.ts", @@ -34,7 +34,7 @@ "isomorphic-fetch": "^3.0.0", "lodash.merge": "^4.6.2", "mime-types": "^2.1.35", - "ts-matches": "^6.1.0", + "ts-matches": "^6.2.1", "yaml": "^2.2.2", "@iarna/toml": "^2.2.5", "@noble/curves": "^1.4.0", diff --git a/system-images/compat/Cargo.toml b/system-images/compat/Cargo.toml index e527c4150..d5fb24161 100644 --- a/system-images/compat/Cargo.toml +++ b/system-images/compat/Cargo.toml @@ -17,7 +17,7 @@ emver = { version = "0.1.7", git = "https://github.com/Start9Labs/emver-rs.git", ] } failure = "0.1.8" indexmap = { version = "1.6.2", features = ["serde"] } -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +imbl-value = "0.1.2" itertools = "0.10.0" lazy_static = "1.4" linear-map = { version = "1.2", features = ["serde_impl"] } diff --git a/web/package-lock.json b/web/package-lock.json index 5ea89c6c8..14abe3b1b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.9", + "version": "0.3.6-alpha.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "startos-ui", - "version": "0.3.6-alpha.9", + "version": "0.3.6-alpha.13", "license": "MIT", "dependencies": { "@angular/animations": "^17.3.1", @@ -118,7 +118,33 @@ "rxjs": ">=7.0.0" } }, - "../sdk/baseDist": {}, + "../sdk/baseDist": { + "name": "@start9labs/start-sdk-base", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime-types": "^2.1.35", + "ts-matches": "^6.2.1", + "yaml": "^2.2.2" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/lodash.merge": "^4.6.2", + "@types/mime-types": "^2.1.4", + "jest": "^29.4.3", + "peggy": "^3.0.2", + "prettier": "^3.2.5", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "ts-pegjs": "^4.2.1", + "tsx": "^4.7.1", + "typescript": "^5.0.4" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -887,9 +913,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -974,13 +1000,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -1176,9 +1202,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -1217,15 +1243,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1307,27 +1333,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", + "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.3" + "@babel/types": "^7.26.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -1707,13 +1733,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2123,13 +2149,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2416,13 +2442,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2432,13 +2458,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -2648,32 +2674,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", + "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", + "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", + "@babel/generator": "^7.26.8", + "@babel/parser": "^7.26.8", + "@babel/template": "^7.26.8", + "@babel/types": "^7.26.8", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2682,14 +2708,14 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", + "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", + "@babel/parser": "^7.26.8", + "@babel/types": "^7.26.8", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -2699,9 +2725,9 @@ } }, "node_modules/@babel/traverse/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -2712,9 +2738,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", + "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", "dev": true, "license": "MIT", "dependencies": { @@ -3312,9 +3338,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -3382,13 +3408,13 @@ "license": "MIT" }, "node_modules/@ljharb/through": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", - "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.14.tgz", + "integrity": "sha512-ajBvlKpWucBB17FuQYUShqpqy8GRgYEpJW0vWJbUu1CV9lWyrDCapy0lScU8T8Z6qn49sSwJB3+M+evYIdGg+A==", "devOptional": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bind": "^1.0.8" }, "engines": { "node": ">= 0.4" @@ -3551,12 +3577,12 @@ } }, "node_modules/@noble/curves": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", - "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.6.0" + "@noble/hashes": "1.7.1" }, "engines": { "node": "^14.21.3 || >=16" @@ -3565,22 +3591,10 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", - "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@noble/hashes": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", - "integrity": "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -3926,9 +3940,9 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz", - "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", "dev": true, "license": "MIT", "dependencies": { @@ -3951,9 +3965,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3994,9 +4008,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", - "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.6.tgz", + "integrity": "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg==", "cpu": [ "arm" ], @@ -4008,9 +4022,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", - "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.6.tgz", + "integrity": "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA==", "cpu": [ "arm64" ], @@ -4022,9 +4036,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", - "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.6.tgz", + "integrity": "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg==", "cpu": [ "arm64" ], @@ -4036,9 +4050,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", - "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.6.tgz", + "integrity": "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg==", "cpu": [ "x64" ], @@ -4050,9 +4064,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", - "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.6.tgz", + "integrity": "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ==", "cpu": [ "arm64" ], @@ -4064,9 +4078,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", - "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.6.tgz", + "integrity": "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ==", "cpu": [ "x64" ], @@ -4078,9 +4092,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", - "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.6.tgz", + "integrity": "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg==", "cpu": [ "arm" ], @@ -4092,9 +4106,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", - "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.6.tgz", + "integrity": "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg==", "cpu": [ "arm" ], @@ -4106,9 +4120,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", - "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.6.tgz", + "integrity": "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA==", "cpu": [ "arm64" ], @@ -4120,9 +4134,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", - "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.6.tgz", + "integrity": "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q==", "cpu": [ "arm64" ], @@ -4134,9 +4148,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", - "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.6.tgz", + "integrity": "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw==", "cpu": [ "loong64" ], @@ -4148,9 +4162,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", - "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.6.tgz", + "integrity": "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ==", "cpu": [ "ppc64" ], @@ -4162,9 +4176,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", - "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.6.tgz", + "integrity": "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg==", "cpu": [ "riscv64" ], @@ -4176,9 +4190,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", - "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.6.tgz", + "integrity": "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw==", "cpu": [ "s390x" ], @@ -4190,9 +4204,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", - "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.6.tgz", + "integrity": "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw==", "cpu": [ "x64" ], @@ -4204,9 +4218,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", - "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.6.tgz", + "integrity": "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A==", "cpu": [ "x64" ], @@ -4218,9 +4232,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", - "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.6.tgz", + "integrity": "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA==", "cpu": [ "arm64" ], @@ -4232,9 +4246,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", - "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.6.tgz", + "integrity": "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA==", "cpu": [ "ia32" ], @@ -4246,9 +4260,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", - "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.6.tgz", + "integrity": "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w==", "cpu": [ "x64" ], @@ -4260,9 +4274,9 @@ ] }, "node_modules/@rollup/wasm-node": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.28.1.tgz", - "integrity": "sha512-t4ckEC09V3wbe0r6T4fGjq85lEbvGcGxn7QYYgjHyKNzZaQU5kFqr4FsavXYHRiVNYq8m+dRhdGjpfcC9UzzPg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/@rollup/wasm-node/-/wasm-node-4.34.6.tgz", + "integrity": "sha512-ox1r6gJiOoyuma9h02LMtMtkyjBvYx3PezgTIk5SL+7Nn1pX0foieMHKDoRw510R8qZ/V2brd/7kQYVda+EDMg==", "dev": true, "license": "MIT", "dependencies": { @@ -4326,13 +4340,13 @@ } }, "node_modules/@sigstore/protobuf-specs": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", - "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz", + "integrity": "sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==", "devOptional": true, "license": "Apache-2.0", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/sign": { @@ -4552,9 +4566,9 @@ } }, "node_modules/@taiga-ui/i18n": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.21.0.tgz", - "integrity": "sha512-T6BQ+qSXh34JpXpNJ9gEzJOjpPM8AchjSuwlZnbZHGo4H7eTRGgt6DdnEBVUIcvGyebVIdBZLSDhpH3lu3c8Yw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-4.23.0.tgz", + "integrity": "sha512-+7AxbAKeQYmS0uK0LqhDqCDwJhuZlNhO4vZ8FTjmgPDWKF/VeRua+v7ei1pp3b6u+n8ADn9wq7Y2oitLjrH2gg==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -4880,9 +4894,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.2.tgz", - "integrity": "sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", + "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -4913,9 +4927,9 @@ "license": "MIT" }, "node_modules/@types/http-proxy": { - "version": "1.17.15", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", - "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "version": "1.17.16", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", + "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", "dev": true, "license": "MIT", "dependencies": { @@ -4971,9 +4985,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.19.67", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz", - "integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==", + "version": "18.19.75", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz", + "integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==", "dev": true, "license": "MIT", "dependencies": { @@ -5018,9 +5032,9 @@ } }, "node_modules/@types/qs": { - "version": "6.9.17", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", - "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", "dev": true, "license": "MIT" }, @@ -5102,9 +5116,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", - "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", "dev": true, "license": "MIT", "dependencies": { @@ -5974,9 +5988,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -5994,9 +6008,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -6108,9 +6122,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.0.tgz", - "integrity": "sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6121,6 +6135,23 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -6141,9 +6172,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001687", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", - "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "dev": true, "funding": [ { @@ -6665,9 +6696,9 @@ } }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6676,13 +6707,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -6774,9 +6805,9 @@ } }, "node_modules/cronstrue": { - "version": "2.52.0", - "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.52.0.tgz", - "integrity": "sha512-NKgHbWkSZXJUcaBHSsyzC8eegD6bBd4O0oCI6XMIJ+y4Bq3v4w7sY3wfWoKPuVlq9pQHRB6od0lmKpIqi8TlKA==", + "version": "2.54.0", + "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.54.0.tgz", + "integrity": "sha512-vyp5NklDxA5MjPfQgkn0bA+0vRQe7Q9keX7RPdV6rMgd7LtDvbuKgnT+3T5ZAX16anSP5HmahcRp8mziXsLfaw==", "license": "MIT", "bin": { "cronstrue": "bin/cli.js" @@ -7216,15 +7247,15 @@ } }, "node_modules/dompurify": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.7.tgz", - "integrity": "sha512-2q4bEI+coQM8f5ez7kt2xclg1XsecaV9ASJk/54vwlfRRNQfDqJz2pzQ8t0Ix/ToBpXlVjrRIx7pFC/o8itG2Q==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", "license": "(MPL-2.0 OR Apache-2.0)" }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7237,13 +7268,13 @@ } }, "node_modules/dunder-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.0.tgz", - "integrity": "sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "devOptional": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, @@ -7273,9 +7304,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.71", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", - "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", + "version": "1.5.96", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", + "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", "dev": true, "license": "ISC" }, @@ -7331,9 +7362,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -7415,12 +7446,25 @@ } }, "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==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT" }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -7637,9 +7681,9 @@ } }, "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==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", "devOptional": true, "license": "Apache-2.0" }, @@ -7759,9 +7803,9 @@ "license": "MIT" }, "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "devOptional": true, "license": "ISC", "dependencies": { @@ -8068,20 +8112,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.5.tgz", - "integrity": "sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "devOptional": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "dunder-proto": "^1.0.0", + "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", + "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", - "hasown": "^2.0.2" + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -8100,6 +8146,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -8470,9 +8530,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", "dev": true, "license": "MIT" }, @@ -8780,9 +8840,9 @@ "license": "MIT" }, "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==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8892,9 +8952,9 @@ } }, "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "devOptional": true, "license": "MIT", "engines": { @@ -8949,9 +9009,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -9229,9 +9289,9 @@ } }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", "bin": { @@ -9446,9 +9506,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.18.tgz", - "integrity": "sha512-okMm/MCoFrm1vByeVFLBdkFIXLSHy/AIK2AEGgY3eoicfWZeOZqv3GfhtQgICkzs/tqorAMm3a4GBg5qNCrqzg==", + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.19.tgz", + "integrity": "sha512-bW/Yp/9dod6fmyR+XqSUL1N5JE7QRxQ3KrBIbYS1FTv32e5i3SEtQVX+71CYNv8maWNSOgnlCoNp9X78f/cKiA==", "license": "MIT", "peer": true }, @@ -9963,9 +10023,9 @@ } }, "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.4.tgz", + "integrity": "sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==", "license": "Apache-2.0" }, "node_modules/lru-cache": { @@ -10078,6 +10138,16 @@ "node": ">= 12" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -10177,9 +10247,9 @@ } }, "node_modules/mime": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", - "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.6.tgz", + "integrity": "sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==", "funding": [ "https://github.com/sponsors/broofa" ], @@ -11044,9 +11114,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, @@ -11255,9 +11325,9 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { @@ -11917,9 +11987,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "dev": true, "funding": [ { @@ -11937,7 +12007,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -12025,9 +12095,9 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.1.0.tgz", - "integrity": "sha512-rm0bdSv4jC3BDma3s9H19ZddW0aHX6EoqwDYU2IfZhRN+53QrufTRo2IdkAbRqLx4R2IYbZnbjKKxg4VN5oU9Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "license": "MIT", "dependencies": { @@ -12075,9 +12145,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -12843,9 +12913,9 @@ } }, "node_modules/rollup": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", - "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "version": "4.34.6", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.6.tgz", + "integrity": "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12859,25 +12929,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.28.1", - "@rollup/rollup-android-arm64": "4.28.1", - "@rollup/rollup-darwin-arm64": "4.28.1", - "@rollup/rollup-darwin-x64": "4.28.1", - "@rollup/rollup-freebsd-arm64": "4.28.1", - "@rollup/rollup-freebsd-x64": "4.28.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", - "@rollup/rollup-linux-arm-musleabihf": "4.28.1", - "@rollup/rollup-linux-arm64-gnu": "4.28.1", - "@rollup/rollup-linux-arm64-musl": "4.28.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", - "@rollup/rollup-linux-riscv64-gnu": "4.28.1", - "@rollup/rollup-linux-s390x-gnu": "4.28.1", - "@rollup/rollup-linux-x64-gnu": "4.28.1", - "@rollup/rollup-linux-x64-musl": "4.28.1", - "@rollup/rollup-win32-arm64-msvc": "4.28.1", - "@rollup/rollup-win32-ia32-msvc": "4.28.1", - "@rollup/rollup-win32-x64-msvc": "4.28.1", + "@rollup/rollup-android-arm-eabi": "4.34.6", + "@rollup/rollup-android-arm64": "4.34.6", + "@rollup/rollup-darwin-arm64": "4.34.6", + "@rollup/rollup-darwin-x64": "4.34.6", + "@rollup/rollup-freebsd-arm64": "4.34.6", + "@rollup/rollup-freebsd-x64": "4.34.6", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", + "@rollup/rollup-linux-arm-musleabihf": "4.34.6", + "@rollup/rollup-linux-arm64-gnu": "4.34.6", + "@rollup/rollup-linux-arm64-musl": "4.34.6", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", + "@rollup/rollup-linux-riscv64-gnu": "4.34.6", + "@rollup/rollup-linux-s390x-gnu": "4.34.6", + "@rollup/rollup-linux-x64-gnu": "4.34.6", + "@rollup/rollup-linux-x64-musl": "4.34.6", + "@rollup/rollup-win32-arm64-msvc": "4.34.6", + "@rollup/rollup-win32-ia32-msvc": "4.34.6", + "@rollup/rollup-win32-x64-msvc": "4.34.6", "fsevents": "~2.3.2" } }, @@ -13026,9 +13096,9 @@ "optional": true }, "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, "license": "MIT", "dependencies": { @@ -13038,7 +13108,7 @@ "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -13393,16 +13463,73 @@ } }, "node_modules/side-channel": { - "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -13650,9 +13777,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", - "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "devOptional": true, "license": "CC0-1.0" }, @@ -13981,17 +14108,17 @@ } }, "node_modules/terser-webpack-plugin": { - "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==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -14015,57 +14142,30 @@ } } }, - "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", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "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/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==", + "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, "license": "MIT" }, - "node_modules/terser-webpack-plugin/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==", + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.1.tgz", + "integrity": "sha512-GWANVlPM/ZfYzuPHjq0nxT+EbOEDDN3Jwhwdg1D8TU8oSkktp8w64Uq4auuGLxFSoNTRDncTq2hQHX1Ld9KHkA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@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.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=10" } }, "node_modules/terser/node_modules/commander": { @@ -14661,9 +14761,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -14682,7 +14782,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/web/package.json b/web/package.json index 9d939cdf1..253edf38d 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "startos-ui", - "version": "0.3.6-alpha.9", + "version": "0.3.6-alpha.13", "author": "Start9 Labs, Inc", "homepage": "https://start9.com/", "license": "MIT", diff --git a/web/projects/setup-wizard/src/app/services/mock-api.service.ts b/web/projects/setup-wizard/src/app/services/mock-api.service.ts index 99be80684..0943cd466 100644 --- a/web/projects/setup-wizard/src/app/services/mock-api.service.ts +++ b/web/projects/setup-wizard/src/app/services/mock-api.service.ts @@ -158,7 +158,7 @@ export class MockApiService extends ApiService { case 3: return { status: 'complete', - torAddress: 'https://asdafsadasdasasdasdfasdfasdf.onion', + torAddresses: ['https://asdafsadasdasasdasdfasdfasdf.onion'], hostname: 'adjective-noun', lanAddress: 'https://adjective-noun.local', rootCa: encodeBase64(rootCA), @@ -313,7 +313,7 @@ export class MockApiService extends ApiService { async complete(): Promise { await pauseFor(1000) return { - torAddress: 'https://asdafsadasdasasdasdfasdfasdf.onion', + torAddresses: ['https://asdafsadasdasasdasdfasdfasdf.onion'], hostname: 'adjective-noun', lanAddress: 'https://adjective-noun.local', rootCa: encodeBase64(rootCA), diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.html b/web/projects/ui/src/app/components/interface-info/interface-info.component.html new file mode 100644 index 000000000..66d773063 --- /dev/null +++ b/web/projects/ui/src/app/components/interface-info/interface-info.component.html @@ -0,0 +1,62 @@ + + + +

{{ iFace.name }}

+

{{ iFace.description }}

+ + Add Domain + + + Make {{ iFace.public ? 'Private' : 'Public' }} + +
+
+
+ + +

{{ address.name }}

+

{{ address.url }}

+ + Remove + + + Remove + +
+ + + + + + + + + + + + + + +
+
diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.scss b/web/projects/ui/src/app/components/interface-info/interface-info.component.scss new file mode 100644 index 000000000..61ead3b94 --- /dev/null +++ b/web/projects/ui/src/app/components/interface-info/interface-info.component.scss @@ -0,0 +1,3 @@ +p { + font-family: 'Courier New'; +} \ No newline at end of file diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.component.ts b/web/projects/ui/src/app/components/interface-info/interface-info.component.ts new file mode 100644 index 000000000..932b1f767 --- /dev/null +++ b/web/projects/ui/src/app/components/interface-info/interface-info.component.ts @@ -0,0 +1,393 @@ +import { Component, Inject, Input } from '@angular/core' +import { WINDOW } from '@ng-web-apis/common' +import { + AlertController, + ModalController, + ToastController, +} from '@ionic/angular' +import { + copyToClipboard, + ErrorService, + LoadingService, +} from '@start9labs/shared' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { PatchDB } from 'patch-db-client' +import { QRComponent } from 'src/app/components/qr/qr.component' +import { firstValueFrom } from 'rxjs' +import { ISB, T, utils } from '@start9labs/start-sdk' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormDialogService } from 'src/app/services/form-dialog.service' +import { FormComponent } from 'src/app/components/form.component' +import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' +import { toAcmeName } from 'src/app/util/acme' +import { ConfigService } from 'src/app/services/config.service' + +export type MappedInterface = T.ServiceInterface & { + addresses: MappedAddress[] + public: boolean +} +export type MappedAddress = { + name: string + url: string + isDomain: boolean + isOnion: boolean + acme: string | null +} + +@Component({ + selector: 'interface-info', + templateUrl: './interface-info.component.html', + styleUrls: ['./interface-info.component.scss'], +}) +export class InterfaceInfoComponent { + @Input() pkgId?: string + @Input() iFace!: MappedInterface + + constructor( + private readonly toastCtrl: ToastController, + private readonly modalCtrl: ModalController, + private readonly errorService: ErrorService, + private readonly loader: LoadingService, + private readonly api: ApiService, + private readonly formDialog: FormDialogService, + private readonly alertCtrl: AlertController, + private readonly patch: PatchDB, + private readonly config: ConfigService, + @Inject(WINDOW) private readonly windowRef: Window, + ) {} + + launch(url: string): void { + this.windowRef.open(url, '_blank', 'noreferrer') + } + + async togglePublic() { + const loader = this.loader + .open(`Making ${this.iFace.public ? 'private' : 'public'}`) + .subscribe() + + const params = { + internalPort: this.iFace.addressInfo.internalPort, + public: !this.iFace.public, + } + + try { + if (this.pkgId) { + await this.api.pkgBindingSetPubic({ + ...params, + host: this.iFace.addressInfo.hostId, + package: this.pkgId, + }) + } else { + await this.api.serverBindingSetPubic(params) + } + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } + + async presentDomainForm() { + const acme = await firstValueFrom(this.patch.watch$('serverInfo', 'acme')) + + const spec = getDomainSpec(Object.keys(acme)) + + this.formDialog.open(FormComponent, { + label: 'Add Domain', + data: { + spec: await configBuilderToSpec(spec), + buttons: [ + { + text: 'Save', + handler: async (val: typeof spec._TYPE) => { + if (val.type.selection === 'standard') { + return this.saveStandard( + val.type.value.domain, + val.type.value.acme, + ) + } else { + return this.saveTor(val.type.value.key) + } + }, + }, + ], + }, + }) + } + + async removeStandard(url: string) { + const loader = this.loader.open('Removing').subscribe() + + const params = { + domain: new URL(url).hostname, + } + + try { + if (this.pkgId) { + await this.api.pkgRemoveDomain({ + ...params, + package: this.pkgId, + host: this.iFace.addressInfo.hostId, + }) + } else { + await this.api.serverRemoveDomain(params) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } + + async removeOnion(url: string) { + const loader = this.loader.open('Removing').subscribe() + + const params = { + onion: new URL(url).hostname, + } + + try { + if (this.pkgId) { + await this.api.pkgRemoveOnion({ + ...params, + package: this.pkgId, + host: this.iFace.addressInfo.hostId, + }) + } else { + await this.api.serverRemoveOnion(params) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } + + async showAcme(url: string | null): Promise { + const alert = await this.alertCtrl.create({ + header: 'ACME Provider', + message: toAcmeName(url), + }) + await alert.present() + } + + async showQR(text: string): Promise { + const modal = await this.modalCtrl.create({ + component: QRComponent, + componentProps: { + text, + }, + cssClass: 'qr-modal', + }) + await modal.present() + } + + async copy(address: string): Promise { + let message = '' + await copyToClipboard(address || '').then(success => { + message = success + ? 'Copied to clipboard!' + : 'Failed to copy to clipboard.' + }) + + const toast = await this.toastCtrl.create({ + header: message, + position: 'bottom', + duration: 1000, + }) + await toast.present() + } + + private async saveStandard(domain: string, acme: string) { + const loader = this.loader.open('Saving').subscribe() + + const params = { + domain, + acme: acme === 'none' ? null : acme, + private: false, + } + + try { + if (this.pkgId) { + await this.api.pkgAddDomain({ + ...params, + package: this.pkgId, + host: this.iFace.addressInfo.hostId, + }) + } else { + await this.api.serverAddDomain(params) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } + + private async saveTor(key: string | null) { + const loader = this.loader.open('Creating onion address').subscribe() + + try { + let onion = key + ? await this.api.addTorKey({ key }) + : await this.api.generateTorKey({}) + onion = `${onion}.onion` + + if (this.pkgId) { + await this.api.pkgAddOnion({ + onion, + package: this.pkgId, + host: this.iFace.addressInfo.hostId, + }) + } else { + await this.api.serverAddOnion({ onion }) + } + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } +} + +function getDomainSpec(acme: string[]) { + return ISB.InputSpec.of({ + type: ISB.Value.union( + { name: 'Type', default: 'standard' }, + ISB.Variants.of({ + standard: { + name: 'Standard', + spec: ISB.InputSpec.of({ + domain: ISB.Value.text({ + name: 'Domain', + description: 'The domain or subdomain you want to use', + placeholder: `e.g. 'mydomain.com' or 'sub.mydomain.com'`, + required: true, + default: null, + patterns: [utils.Patterns.domain], + }), + acme: ISB.Value.select({ + name: 'ACME Provider', + description: + 'Select which ACME provider to use for obtaining your SSL certificate. Add new ACME providers in the System tab. Optionally use your system Root CA. Note: only devices that have trusted your Root CA will be able to access the domain without security warnings.', + values: acme.reduce( + (obj, url) => ({ + ...obj, + [url]: toAcmeName(url), + }), + { none: 'None (use system Root CA)' } as Record, + ), + default: '', + }), + }), + }, + onion: { + name: 'Onion', + spec: ISB.InputSpec.of({ + key: ISB.Value.text({ + name: 'Private Key (optional)', + description: + 'Optionally provide a base64-encoded ed25519 private key for generating the Tor V3 (.onion) address. If not provided, a random key will be generated and used.', + required: false, + default: null, + patterns: [utils.Patterns.base64], + }), + }), + }, + }), + ), + }) +} + +export function getAddresses( + serviceInterface: T.ServiceInterface, + host: T.Host, + config: ConfigService, +): MappedAddress[] { + const addressInfo = serviceInterface.addressInfo + + let hostnames = host.hostnameInfo[addressInfo.internalPort] + + hostnames = hostnames.filter( + h => + config.isLocalhost() || + h.kind !== 'ip' || + h.hostname.kind !== 'ipv6' || + !h.hostname.value.startsWith('fe80::'), + ) + if (config.isLocalhost()) { + const local = hostnames.find( + h => h.kind === 'ip' && h.hostname.kind === 'local', + ) + if (local) { + hostnames.unshift({ + kind: 'ip', + networkInterfaceId: 'lo', + public: false, + hostname: { + kind: 'local', + port: local.hostname.port, + sslPort: local.hostname.sslPort, + value: 'localhost', + }, + }) + } + } + const mappedAddresses = hostnames.flatMap(h => { + let name = '' + let isDomain = false + let isOnion = false + let acme: string | null = null + + if (h.kind === 'onion') { + name = `Tor` + isOnion = true + } else { + const hostnameKind = h.hostname.kind + + if (hostnameKind === 'domain') { + name = 'Domain' + isDomain = true + acme = host.domains[h.hostname.domain]?.acme + } else { + name = + hostnameKind === 'local' + ? 'Local' + : `${h.networkInterfaceId} (${hostnameKind})` + } + } + + const addresses = utils.addressHostToUrl(addressInfo, h) + if (addresses.length > 1) { + return addresses.map(url => ({ + name: `${name} (${new URL(url).protocol + .replace(':', '') + .toUpperCase()})`, + url, + isDomain, + isOnion, + acme, + })) + } else { + return addresses.map(url => ({ + name, + url, + isDomain, + isOnion, + acme, + })) + } + }) + + return mappedAddresses.filter( + (value, index, self) => index === self.findIndex(t => t.url === value.url), + ) +} diff --git a/web/projects/ui/src/app/components/interface-info/interface-info.module.ts b/web/projects/ui/src/app/components/interface-info/interface-info.module.ts new file mode 100644 index 000000000..c31a6ae07 --- /dev/null +++ b/web/projects/ui/src/app/components/interface-info/interface-info.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { IonicModule } from '@ionic/angular' +import { InterfaceInfoComponent } from './interface-info.component' + +@NgModule({ + declarations: [InterfaceInfoComponent], + imports: [CommonModule, IonicModule], + exports: [InterfaceInfoComponent], +}) +export class InterfaceInfoModule {} diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts b/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts new file mode 100644 index 000000000..f00171f05 --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/acme/acme.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { IonicModule } from '@ionic/angular' +import { RouterModule, Routes } from '@angular/router' +import { ACMEPage } from './acme.page' + +const routes: Routes = [ + { + path: '', + component: ACMEPage, + }, +] + +@NgModule({ + imports: [CommonModule, IonicModule, RouterModule.forChild(routes)], + declarations: [ACMEPage], +}) +export class ACMEPageModule {} diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html new file mode 100644 index 000000000..ab7ad3a01 --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.html @@ -0,0 +1,53 @@ + + + + + + ACME + + + + + + + + +

+ Register with one or more ACME providers such as Let's Encrypt in + order to generate SSL (https) certificates on-demand for clearnet + hosting + + View instructions + +

+ + + + Saved Providers + + + + + + Add Provider + + + + + + +

{{ toAcmeName(provider.url) }}

+

Contact: {{ provider.contactString }}

+
+ + + + + + + + +
+
+ + diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.scss b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.scss new file mode 100644 index 000000000..e69de29bb diff --git a/web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts new file mode 100644 index 000000000..5416194aa --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/acme/acme.page.ts @@ -0,0 +1,179 @@ +import { Component } from '@angular/core' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { PatchDB } from 'patch-db-client' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { DataModel } from '../../../services/patch-db/data-model' +import { FormDialogService } from '../../../services/form-dialog.service' +import { FormComponent } from '../../../components/form.component' +import { configBuilderToSpec } from '../../../util/configBuilderToSpec' +import { ISB, utils } from '@start9labs/start-sdk' +import { knownACME, toAcmeName } from 'src/app/util/acme' +import { map } from 'rxjs' + +@Component({ + selector: 'acme', + templateUrl: 'acme.page.html', + styleUrls: ['acme.page.scss'], +}) +export class ACMEPage { + readonly docsUrl = 'https://docs.start9.com/0.3.6/user-manual/acme' + + acme$ = this.patch.watch$('serverInfo', 'acme').pipe( + map(acme => { + const providerUrls = Object.keys(acme) + return providerUrls.map(url => { + const contact = acme[url].contact.map(mailto => + mailto.replace('mailto:', ''), + ) + return { + url, + contact, + contactString: contact.join(', '), + } + }) + }), + ) + + toAcmeName = toAcmeName + + constructor( + private readonly loader: LoadingService, + private readonly errorService: ErrorService, + private readonly api: ApiService, + private readonly patch: PatchDB, + private readonly formDialog: FormDialogService, + ) {} + + async addAcme( + providers: { + url: string + contact: string[] + contactString: string + }[], + ) { + this.formDialog.open(FormComponent, { + label: 'Add ACME Provider', + data: { + spec: await configBuilderToSpec( + getAddAcmeSpec(providers.map(p => p.url)), + ), + buttons: [ + { + text: 'Save', + handler: async ( + val: ReturnType['_TYPE'], + ) => { + const providerUrl = + val.provider.selection === 'other' + ? val.provider.value.url + : val.provider.selection + + return this.saveAcme(providerUrl, val.contact) + }, + }, + ], + }, + }) + } + + async editAcme(provider: string, contact: string[]) { + this.formDialog.open(FormComponent, { + label: 'Edit ACME Provider', + data: { + spec: await configBuilderToSpec(editAcmeSpec), + buttons: [ + { + text: 'Save', + handler: async (val: typeof editAcmeSpec._TYPE) => + this.saveAcme(provider, val.contact), + }, + ], + value: { contact }, + }, + }) + } + + async removeAcme(provider: string) { + const loader = this.loader.open('Removing').subscribe() + + try { + await this.api.removeAcme({ provider }) + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } + + private async saveAcme(providerUrl: string, contact: string[]) { + console.log(providerUrl, contact) + const loader = this.loader.open('Saving').subscribe() + + try { + await this.api.initAcme({ + provider: new URL(providerUrl).href, + contact: contact.map(address => `mailto:${address}`), + }) + return true + } catch (e: any) { + this.errorService.handleError(e) + return false + } finally { + loader.unsubscribe() + } + } +} + +const emailListSpec = ISB.Value.list( + ISB.List.text( + { + name: 'Contact Emails', + description: + 'Needed to obtain a certificate from a Certificate Authority', + minLength: 1, + }, + { + inputmode: 'email', + patterns: [utils.Patterns.email], + }, + ), +) + +function getAddAcmeSpec(providers: string[]) { + const availableAcme = knownACME.filter(acme => !providers.includes(acme.url)) + + return ISB.InputSpec.of({ + provider: ISB.Value.union( + { name: 'Provider', default: (availableAcme[0]?.url as any) || 'other' }, + ISB.Variants.of({ + ...availableAcme.reduce( + (obj, curr) => ({ + ...obj, + [curr.url]: { + name: curr.name, + spec: ISB.InputSpec.of({}), + }, + }), + {}, + ), + other: { + name: 'Other', + spec: ISB.InputSpec.of({ + url: ISB.Value.text({ + name: 'URL', + default: null, + required: true, + inputmode: 'url', + patterns: [utils.Patterns.url], + }), + }), + }, + }), + ), + contact: emailListSpec, + }) +} + +const editAcmeSpec = ISB.InputSpec.of({ + contact: emailListSpec, +}) diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.module.ts b/web/projects/ui/src/app/pages/server-routes/email/email.module.ts new file mode 100644 index 000000000..f6b0c735d --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/email/email.module.ts @@ -0,0 +1,42 @@ +import { NgModule } from '@angular/core' +import { CommonModule } from '@angular/common' +import { Routes, RouterModule } from '@angular/router' +import { TuiInputModule } from '@taiga-ui/kit' +import { + TuiNotificationModule, + TuiTextfieldControllerModule, +} from '@taiga-ui/core' +import { FormsModule, ReactiveFormsModule } from '@angular/forms' +import { EmailPage } from './email.page' +import { FormModule } from 'src/app/components/form/form.module' +import { IonicModule } from '@ionic/angular' +import { TuiErrorModule, TuiModeModule } from '@taiga-ui/core' +import { TuiAppearanceModule, TuiButtonModule } from '@taiga-ui/experimental' + +const routes: Routes = [ + { + path: '', + component: EmailPage, + }, +] + +@NgModule({ + imports: [ + CommonModule, + IonicModule, + RouterModule.forChild(routes), + CommonModule, + FormsModule, + ReactiveFormsModule, + TuiButtonModule, + TuiInputModule, + FormModule, + TuiNotificationModule, + TuiTextfieldControllerModule, + TuiAppearanceModule, + TuiModeModule, + TuiErrorModule, + ], + declarations: [EmailPage], +}) +export class EmailPageModule {} diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.html b/web/projects/ui/src/app/pages/server-routes/email/email.page.html new file mode 100644 index 000000000..5e0e58fa4 --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/email/email.page.html @@ -0,0 +1,70 @@ + + + Email + + + + + + + + + Fill out the form below to connect to an external SMTP server. With your + permission, installed services can use the SMTP server to send emails. To + grant permission to a particular service, visit that service's "Actions" + page. Not all services support sending emails. + + View instructions + + + +
+

SMTP Credentials

+ + + +
+
+

Send Test Email

+ + To Address + + + +
+
+
diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.scss b/web/projects/ui/src/app/pages/server-routes/email/email.page.scss new file mode 100644 index 000000000..b15986fc9 --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/email/email.page.scss @@ -0,0 +1,9 @@ +form { + padding-top: 24px; + margin: auto; + max-width: 30rem; +} + +h3 { + display: flex; +} \ No newline at end of file diff --git a/web/projects/ui/src/app/pages/server-routes/email/email.page.ts b/web/projects/ui/src/app/pages/server-routes/email/email.page.ts new file mode 100644 index 000000000..e52bf32dd --- /dev/null +++ b/web/projects/ui/src/app/pages/server-routes/email/email.page.ts @@ -0,0 +1,83 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { ErrorService, LoadingService } from '@start9labs/shared' +import { IST, inputSpec } from '@start9labs/start-sdk' +import { TuiDialogService } from '@taiga-ui/core' +import { PatchDB } from 'patch-db-client' +import { switchMap, tap } from 'rxjs' +import { ApiService } from 'src/app/services/api/embassy-api.service' +import { FormService } from 'src/app/services/form.service' +import { DataModel } from 'src/app/services/patch-db/data-model' +import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec' + +@Component({ + selector: 'email-page', + templateUrl: './email.page.html', + styleUrls: ['./email.page.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class EmailPage { + private readonly dialogs = inject(TuiDialogService) + private readonly loader = inject(LoadingService) + private readonly errorService = inject(ErrorService) + private readonly formService = inject(FormService) + private readonly patch = inject>(PatchDB) + private readonly api = inject(ApiService) + + isSaved = false + testAddress = '' + + readonly spec: Promise = configBuilderToSpec( + inputSpec.constants.customSmtp, + ) + readonly form$ = this.patch.watch$('serverInfo', 'smtp').pipe( + tap(value => (this.isSaved = !!value)), + switchMap(async value => + this.formService.createForm(await this.spec, value), + ), + ) + + async save( + value: typeof inputSpec.constants.customSmtp._TYPE | null, + ): Promise { + const loader = this.loader.open('Saving...').subscribe() + + try { + if (value) { + await this.api.setSmtp(value) + this.isSaved = true + } else { + await this.api.clearSmtp({}) + this.isSaved = false + } + } catch (e: any) { + this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + } + + async sendTestEmail(value: typeof inputSpec.constants.customSmtp._TYPE) { + const loader = this.loader.open('Sending email...').subscribe() + + try { + await this.api.testSmtp({ + to: this.testAddress, + ...value, + }) + } catch (e: any) { + return this.errorService.handleError(e) + } finally { + loader.unsubscribe() + } + + this.dialogs + .open( + `A test email has been sent to ${this.testAddress}.

Check your spam folder and mark as not spam`, + { + label: 'Success', + size: 's', + }, + ) + .subscribe() + } +} diff --git a/web/projects/ui/src/app/routes/portal/components/form/form-select/form-select.component.html b/web/projects/ui/src/app/routes/portal/components/form/form-select/form-select.component.html index 9149e8844..b8e79351a 100644 --- a/web/projects/ui/src/app/routes/portal/components/form/form-select/form-select.component.html +++ b/web/projects/ui/src/app/routes/portal/components/form/form-select/form-select.component.html @@ -7,7 +7,7 @@ [(ngModel)]="selected" (focusedChange)="onFocus($event)" > - {{ spec.name }}* + {{ spec.name }} *