From 7461e6b5b2d817c69f44b02af642050c99aff130 Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Tue, 14 Oct 2025 16:18:21 -0400 Subject: [PATCH 1/7] add missing files and updated README --- .gitignore | 8 +- container-creation/README.md | 84 +++- .../container-creation/etc-ssh/sshd_config | 121 ++++++ .../root-bin/cleanup/pruneTempFiles.sh | 68 ++++ .../root-bin/create-lxc-container.sh | 315 ++++++++++++++ .../root-bin/deploy-application.sh | 232 +++++++++++ .../deployment-scripts/gatherEnvVars.sh | 111 +++++ .../deployment-scripts/gatherRuntimeLangs.sh | 105 +++++ .../deployment-scripts/gatherServices.sh | 158 ++++++++ .../deployment-scripts/gatherSetupCommands.sh | 57 +++ .../root-bin/js/authenticateRepo.js | 23 ++ .../root-bin/js/authenticateUser.js | 28 ++ .../root-bin/js/package-lock.json | 383 ++++++++++++++++++ .../root-bin/js/package.json | 7 + .../container-creation/root-bin/js/runner.js | 13 + .../root-bin/opensource-server | 1 + .../protocols/master_protocol_list.txt | 145 +++++++ .../root-bin/services/service_map_debian.json | 67 +++ .../root-bin/services/service_map_rocky.json | 96 +++++ .../root-bin/ssh/detectPublicKey.sh | 23 ++ .../root-bin/ssh/publicKeyAppendJumpHost.sh | 12 + .../container-creation/root-bin/test.sh | 46 +++ .../intern-phxdc-pve1/etc-ssh/sshd_config | 141 +++++++ .../extract-fingerprint.sh | 39 ++ nginx-reverse-proxy/checkHostname.js | 22 + nginx-reverse-proxy/checkHostnameRunner.js | 9 + 26 files changed, 2307 insertions(+), 7 deletions(-) create mode 100644 container-creation/container-creation/etc-ssh/sshd_config create mode 100755 container-creation/container-creation/root-bin/cleanup/pruneTempFiles.sh create mode 100755 container-creation/container-creation/root-bin/create-lxc-container.sh create mode 100755 container-creation/container-creation/root-bin/deploy-application.sh create mode 100644 container-creation/container-creation/root-bin/deployment-scripts/gatherEnvVars.sh create mode 100755 container-creation/container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh create mode 100755 container-creation/container-creation/root-bin/deployment-scripts/gatherServices.sh create mode 100644 container-creation/container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh create mode 100644 container-creation/container-creation/root-bin/js/authenticateRepo.js create mode 100644 container-creation/container-creation/root-bin/js/authenticateUser.js create mode 100644 container-creation/container-creation/root-bin/js/package-lock.json create mode 100644 container-creation/container-creation/root-bin/js/package.json create mode 100644 container-creation/container-creation/root-bin/js/runner.js create mode 120000 container-creation/container-creation/root-bin/opensource-server create mode 100644 container-creation/container-creation/root-bin/protocols/master_protocol_list.txt create mode 100644 container-creation/container-creation/root-bin/services/service_map_debian.json create mode 100644 container-creation/container-creation/root-bin/services/service_map_rocky.json create mode 100755 container-creation/container-creation/root-bin/ssh/detectPublicKey.sh create mode 100755 container-creation/container-creation/root-bin/ssh/publicKeyAppendJumpHost.sh create mode 100755 container-creation/container-creation/root-bin/test.sh create mode 100644 container-creation/intern-phxdc-pve1/etc-ssh/sshd_config create mode 100755 container-creation/intern-phxdc-pve1/home-create-container-bin/extract-fingerprint.sh create mode 100644 nginx-reverse-proxy/checkHostname.js create mode 100644 nginx-reverse-proxy/checkHostnameRunner.js diff --git a/.gitignore b/.gitignore index d5300330..37d7e734 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ -Wazuh/node_modules/ -Wazuh/package-lock.json -Wazuh/package.json -Wazuh/.env -Wazuh/.gitignore - +node_modules +.env diff --git a/container-creation/README.md b/container-creation/README.md index a2ca2a56..b0ea8a57 100644 --- a/container-creation/README.md +++ b/container-creation/README.md @@ -1 +1,83 @@ -# Container Creation \ No newline at end of file +# Container Creation + +This document describes the container creation flow used by the MIE Open Source container provisioning system. The sequence below captures the full end-to-end flow from an operator connecting to the jump host through to the container being created, configured, and started on the hypervisor. + +High-level summary: + +- Operators connect to the `intern-phxdc-pve1` jump host as the `create-container` user. +- A chain of SSH restrictions and environment-variable handoffs lead the request to the container-creation server, which runs scripts that validate credentials, check hostname availability, prepare deployment artifacts, and finally invoke the hypervisor-side scripts that clone and start the container. + +Sequence diagram (Mermaid): + +```mermaid +sequenceDiagram + actor User as Operator + participant Jump as intern-phxdc-pve1 (create-container) + participant Extract as extract-fingerprint.sh + participant ProxySSH as ProxySSH (env passthrough) + participant ContainerServer as container-creation (10.15.234.122) + participant Sudo as create-lxc-container.sh (runs as root) + participant JSRunner as js/runner.js + participant Auth as js/authenticateUser.js + participant NGINX as NGINX (port_map.json check) + participant MasterProto as protocols/master_protocol_list.txt + participant DeployApp as deploy-application.sh + participant RepoCheck as curl (repo existence) + participant Gather as gather*.sh + participant SFTP as sftp to hypervisor + participant Hypervisor as intern-phxdc-pve1 (hypervisor-side scripts) + participant CreateCT as create-container.sh + participant LDAP as configureLDAP.sh + participant Pown as pown.sh + participant Wazuh as register-agent.sh + participant DeployOnStart as deployOnStart.sh + participant RuntimeInstall as node/python runtime installers + + User->>Jump: ssh create-container@opensource.mieweb.org + Jump->>Extract: run extract-fingerprint.sh per sshd_config + Extract->>ProxySSH: pass certain ENV variables + ProxySSH->>ContainerServer: auto-ssh into 10.15.234.122 as create-container + ContainerServer->>Sudo: sshd_config triggers create-lxc-container.sh (runs as root via sudo) + Sudo->>Sudo: prompt for variables not supplied via ENV + Sudo->>JSRunner: invoke js/runner.js -> js/authenticateUser.js to validate credentials + Sudo->>NGINX: SSH to NGINX and run checkHostnameRunner.js -> checkHostname.js (validate hostname via port_map.json) + Sudo->>MasterProto: compare extra protocols against master_protocol_list.txt + Sudo->>DeployApp: if automatic deployment selected, call deploy-application.sh + DeployApp->>DeployApp: prompt for unset env vars + DeployApp->>RepoCheck: curl to check repository exists and is public + DeployApp->>JSRunner: invoke js/runner.js -> authenticateRepo.js + DeployApp->>Gather: invoke gatherSetupCommands.sh, gatherEnvVars.sh, gatherRuntimeLangs.sh, gatherServices.sh + Sudo->>SFTP: send user's public key, protocol file, env vars, and services to hypervisor via sftp + Sudo->>Hypervisor: invoke create-container.sh on intern-phxdc-pve1 over ssh + Hypervisor->>CreateCT: create-container.sh assigns CTID, clones CT template, sets tags, ACLs, waits for networking + CreateCT->>CreateCT: copy user's public key into container, generate random root password + CreateCT->>LDAP: run configureLDAP.sh + LDAP->>Pown: configureLDAP clones and runs pown.sh + CreateCT->>Wazuh: run register-agent.sh to install/configure wazuh manager IP + CreateCT->>DeployOnStart: call deployOnStart.sh if deployment enabled + DeployOnStart->>RepoCheck: clone project repo, copy env to .env + DeployOnStart->>RuntimeInstall: call node_runtime_install.sh or python_runtime_install.sh + RuntimeInstall->>RuntimeInstall: compile and install language runtime from source + DeployOnStart->>DeployOnStart: start services + CreateCT->>CreateCT: register container ports via register-container.sh + CreateCT->>CreateCT: launch tmux that invokes start_services.sh + +``` + +Notes: + +- The diagram closely follows the implementation: SSH restrictions are enforced via per-user `sshd_config` command= clauses and `extract-fingerprint.sh` establishes the environment and fingerprint forwarding. +- The system performs multiple, layered checks (user auth, hostname availability, protocol validation, repo existence) to avoid failed deployments. +- Several scripts prompt for missing variables so interactive runs are possible; these could be converted to fully-noninteractive flows by providing all required env vars. + +References: + +- See the `create-a-container/` folder for the front-end and server that initiate the flow. +- See the `container-creation/` folder for the server-side scripts invoked during provisioning. + +If you'd like, I can also: + +- Add a simple diagram PNG export (requires mermaid-cli) and include it in the repo. +- Turn the interactive prompts into optional CLI flags for automation. + + diff --git a/container-creation/container-creation/etc-ssh/sshd_config b/container-creation/container-creation/etc-ssh/sshd_config new file mode 100644 index 00000000..08451e70 --- /dev/null +++ b/container-creation/container-creation/etc-ssh/sshd_config @@ -0,0 +1,121 @@ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +Include /etc/ssh/sshd_config.d/*.conf + +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +PermitRootLogin yes +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +PubkeyAuthentication yes + +# Expect .ssh/authorized_keys2 to be disregarded by default in future. +#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication yes +#PermitEmptyPasswords no + +# Change to yes to enable challenge-response passwords (beware issues with +# some PAM modules and threads) +KbdInteractiveAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes +#GSSAPIStrictAcceptorCheck yes +#GSSAPIKeyExchange no + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the KbdInteractiveAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via KbdInteractiveAuthentication may bypass +# the setting of "PermitRootLogin prohibit-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and KbdInteractiveAuthentication to 'no'. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +X11Forwarding yes +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +PrintMotd no +#PrintLastLog yes +#TCPKeepAlive yes +PermitUserEnvironment yes +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS no +#PidFile /run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* LINUX_DISTRIBUTION SERVICES CUSTOM_SERVICES REQUIRE_SERVICES + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server + +# Example of overriding settings on a per-user basis +Match User create-container + AcceptEnv SSH_KEY_FP PUBLIC_KEY PROXMOX_USERNAME PROXMOX_PASSWORD CONTAINER_NAME CONTAINER_PASSWORD HTTP_PORT DEPLOY_ON_START PROJECT_REPOSITORY PROJECT_BRANCH PROJECT_ROOT REQUIRE_ENV_VARS CONTAINER_ENV_VARS INSTALL_COMMAND BUILD_COMMAND START_COMMAND RUNTIME_LANGUAGE SERVICES REQUIRE_SERVICES CUSTOM_SERVICES LINUX_DISTRIBUTION MULTI_COMPONENT ROOT_START_COMMAND GH_ACTION GITHUB_PAT SELF_HOSTED_RUNNER + ForceCommand sudo -E /root/bin/create-lxc-container.sh + PermitTTY yes diff --git a/container-creation/container-creation/root-bin/cleanup/pruneTempFiles.sh b/container-creation/container-creation/root-bin/cleanup/pruneTempFiles.sh new file mode 100755 index 00000000..636e22b1 --- /dev/null +++ b/container-creation/container-creation/root-bin/cleanup/pruneTempFiles.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Script to prune all temporary files (env vars, protocols, services, and public keys) +# Last Updated July 28th 2025 Maxwell Klema + +LOG_FILE="/var/log/pruneTempFiles.log" + +writeLog() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')]: $1" >> "$LOG_FILE" +} + +# Function to remove temporary environment variable Folders +removeTempEnvVars() { + TEMP_ENV_FOLDER="/root/bin/env" + while read -r line; do + if [[ "$line" == /root/bin/env/env_* ]]; then + rm -rf "$line" > /dev/null 2>&1 + writeLog "Removed temporary environment variable folder: $line" + fi + done < <(find "$TEMP_ENV_FOLDER" -maxdepth 1 -type d -name "env_*") +} + +# Function to remove temporary services file +removeTempServices() { + TEMP_SERVICES_FOLDER="/root/bin/services" + while read -r line; do + if [[ "$line" == /root/bin/services/services_* ]]; then + rm -f "$line" + writeLog "Removed temporary services file: $line" + fi + done < <(find "$TEMP_SERVICES_FOLDER" -maxdepth 1 -type f -name "services_*") +} + +# Function to remove temporary public key files +removeTempPublicKeys() { + TEMP_PUB_FOLDER="/root/bin/ssh/temp_pubs" + while read -r line; do + if [[ "$line" == /root/bin/ssh/temp_pubs/key_* ]]; + then + rm -f "$line" + writeLog "Removed temporary public key file: $line" + fi + done < <(find "$TEMP_PUB_FOLDER" -maxdepth 1 -type f -name "key_*") +} + +# Function to remove temporary protocol files +removeTempProtocols() { + TEMP_PROTOCOL_FOLDER="/root/bin/protocols" + while read -r line; do + if [[ "$line" == /root/bin/protocols/protocol_list* ]]; then + rm -f "$line" + writeLog "Removed temporary protocol file: $line" + fi + done < <(find "$TEMP_PROTOCOL_FOLDER" -maxdepth 1 -type f -name "protocol_list*") +} + +# Main function to prune all temporary files +pruneTempFiles() { + writeLog "Starting to prune temporary files..." + removeTempEnvVars + removeTempServices + removeTempPublicKeys + removeTempProtocols + writeLog "Finished pruning temporary files." +} + +# Execute the main function +pruneTempFiles +exit 0 \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/create-lxc-container.sh b/container-creation/container-creation/root-bin/create-lxc-container.sh new file mode 100755 index 00000000..a1cbe5d1 --- /dev/null +++ b/container-creation/container-creation/root-bin/create-lxc-container.sh @@ -0,0 +1,315 @@ +#!/bin/bash +# Main Container Creation Script +# Modified July 28th, 2025 by Maxwell Klema +# ------------------------------------------ + +LOG_FILE="/var/log/create-container.log" + +writeLog() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')]: $1" >> "$LOG_FILE" +} + +# Define color variables (works on both light and dark backgrounds) +RESET="\033[0m" +BOLD="\033[1m" +MAGENTA='\033[35m' + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}📦 MIE Container Creation Script ${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +# Authenticate User (Only Valid Users can Create Containers) + +outputError() { + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + echo -e "${BOLD}${MAGENTA}❌ Script Failed. Exiting... ${RESET}" + echo -e "$1" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +} + +writeLog "Starting Container Creation Script" + +if [ -z "$PROXMOX_USERNAME" ]; then + read -p "Enter Proxmox Username → " PROXMOX_USERNAME +fi + +if [ -z "$PROXMOX_PASSWORD" ]; then + read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD + echo "" +fi + +USER_AUTHENTICATED=$(node /root/bin/js/runner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD") + +while [ $USER_AUTHENTICATED == 'false' ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Proxmox Credentials." + writeLog "Invalid Proxmox credentials entered for user: $PROXMOX_USERNAME (GH_ACTION mode)" + exit 2 + fi + echo "❌ Authentication Failed. Try Again" + writeLog "Invalid Proxmox credentials entered for user: $PROXMOX_USERNAME" + read -p "Enter Proxmox Username → " PROXMOX_USERNAME + read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD + echo "" + + USER_AUTHENTICATED=$(node /root/bin/js/runner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD") +done + +echo "🎉 Your proxmox account, $PROXMOX_USERNAME@pve, has been authenticated" + +# Gather Container Hostname (hostname.opensource.mieweb.org) ===== + +if [ -z "$CONTAINER_NAME" ]; then + read -p "Enter Application Name (One-Word) → " CONTAINER_NAME +fi + +CONTAINER_NAME="${CONTAINER_NAME,,}" #convert to lowercase +HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}") + +while [[ $HOST_NAME_EXISTS == 'true' ]] || ! [[ "$CONTAINER_NAME" =~ ^[A-Za-z0-9-]+$ ]]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Container Hostname." + writeLog "Invalid container hostname entered: $CONTAINER_NAME (GH_ACTION mode)" + exit 3 + fi + echo "Sorry! Either that name has already been registered or your hostname is ill-formatted. Try another name" + writeLog "Invalid container hostname entered: $CONTAINER_NAME (already exists or ill-formatted)" + read -p "Enter Application Name (One-Word) → " CONTAINER_NAME + HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}") + CONTAINER_NAME="${CONTAINER_NAME,,}" +done + +echo "✅ $CONTAINER_NAME is available" + +# Choose Linux Distribution + +if [ -z "$LINUX_DISTRIBUTION" ]; then + echo "🐧 Available Linux Distributions:" + echo "1. Debian 12 (Bookworm)" + echo "2. Rocky 9 " + read -p "➡️ Choose a Linux Distribution (debian/rocky) → " LINUX_DISTRIBUTION +fi + +if [ "${LINUX_DISTRIBUTION,,}" != "debian" ] && [ "${LINUX_DISTRIBUTION,,}" != "rocky" ]; then + LINUX_DISTRIBUTION="debian" +fi + +LINUX_DISTRIBUTION=${LINUX_DISTRIBUTION,,} + +# Attempt to detect public keys + +echo -e "\n🔑 Attempting to Detect SSH Public Key..." + +AUTHORIZED_KEYS="/root/.ssh/authorized_keys" +RANDOM_NUM=$(shuf -i 100000-999999 -n 1) +PUB_FILE="key_$RANDOM_NUM.pub" +TEMP_PUB_FILE="/root/bin/ssh/temp_pubs/$PUB_FILE" # in case two users are running this script at the same time, they do not overwrite each other's temp files +touch "$TEMP_PUB_FILE" +DETECT_PUBLIC_KEY=$(sudo /root/bin/ssh/detectPublicKey.sh "$SSH_KEY_FP" "$TEMP_PUB_FILE") + +if [ "$DETECT_PUBLIC_KEY" == "Public key found for create-container" ]; then + echo "🔐 Public Key Found!" +else + echo "🔍 Could not detect Public Key" + + if [ -z "$PUBLIC_KEY" ]; then + read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY + fi + + # Check if key is valid + + while [[ "$PUBLIC_KEY" != "" && $(echo "$PUBLIC_KEY" | ssh-keygen -l -f - 2>&1 | tr -d '\r') == "(stdin) is not a public key file." ]]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Public Key" + writeLog "Invalid public key entered (GH_ACTION mode)" + exit 5 + fi + echo "❌ \"$PUBLIC_KEY\" is not a valid key. Enter either a valid key or leave blank to skip." + writeLog "Invalid public key entered: $PUBLIC_KEY" + read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY + done + + if [ "$PUBLIC_KEY" != "" ]; then + echo "$PUBLIC_KEY" > "$AUTHORIZED_KEYS" && systemctl restart ssh + echo "$PUBLIC_KEY" > "$TEMP_PUB_FILE" + sudo /root/bin/ssh/publicKeyAppendJumpHost.sh "$PUBLIC_KEY" + fi +fi + +# Get HTTP Port Container Listens On + +if [ -z "$HTTP_PORT" ]; then + read -p "Enter HTTP Port for your container to listen on (80-60000) → " HTTP_PORT + if [ "${GH_ACTION^^}" == "Y" ]; then + HTTP_PORT="3000" # Default to 3000 if not set + fi +fi + +while ! [[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || [ "$HTTP_PORT" -lt 80 ] || [ "$HTTP_PORT" -gt 60000 ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid HTTP Port. Must be between 80 and 60,000." + writeLog "Invalid HTTP port entered: $HTTP_PORT (GH_ACTION mode)" + exit 6 + fi + echo "❌ Invalid HTTP Port. It must be a number between 80 and 60,000." + writeLog "Invalid HTTP port entered: $HTTP_PORT" + read -p "Enter HTTP Port for your container to listen on (80-60000) → " HTTP_PORT +done + +echo "✅ HTTP Port is set to $HTTP_PORT" + +# Get any other protocols + +protocol_duplicate() { + PROTOCOL="$1" + shift #remaining params are part of list + LIST="$@" + + for item in $LIST; do + if [[ "$item" == "$PROTOCOL" ]]; then + return 0 # Protocol is a duplicate + fi + done + return 1 # Protocol is not a duplicate +} + +read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS +while [ "${USE_OTHER_PROTOCOLS^^}" != "Y" ] && [ "${USE_OTHER_PROTOCOLS^^}" != "N" ] && [ "${USER_OTHER_PROTOCOLS^^}" != "" ]; do + echo "Please answer 'y' for yes or 'n' for no." + read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS +done + +if [ "${USE_OTHER_PROTOCOLS^^}" == "Y" ]; then + + RANDOM_NUM=$(shuf -i 100000-999999 -n 1) + PROTOCOL_BASE_FILE="protocol_list_$RANDOM_NUM.txt" + PROTOCOL_FILE="/root/bin/protocols/$PROTOCOL_BASE_FILE" + touch "$PROTOCOL_FILE" + + LIST_PROTOCOLS=() + read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"e\" to exit → " PROTOCOL_NAME + while [ "${PROTOCOL_NAME^^}" != "E" ]; do + FOUND=0 #keep track if protocol was found + while read line; do + PROTOCOL_ABBRV=$(echo "$line" | awk '{print $1}') + protocol_duplicate "$PROTOCOL_ABBRV" "${LIST_PROTOCOLS[@]}" + IS_PROTOCOL_DUPLICATE=$? + if [[ "$PROTOCOL_ABBRV" == "${PROTOCOL_NAME^^}" && "$IS_PROTOCOL_DUPLICATE" -eq 1 ]]; then + LIST_PROTOCOLS+=("$PROTOCOL_ABBRV") + PROTOCOL_UNDRLYING_NAME=$(echo "$line" | awk '{print $3}') + PROTOCOL_DEFAULT_PORT=$(echo "$line" | awk '{print $2}') + echo "$PROTOCOL_ABBRV $PROTOCOL_UNDRLYING_NAME $PROTOCOL_DEFAULT_PORT" >> "$PROTOCOL_FILE" + echo "✅ Protocol ${PROTOCOL_NAME^^} added to container." + FOUND=1 #protocol was found + break + else + echo "❌ Protocol ${PROTOCOL_NAME^^} was already added to your container. Please try again." + FOUND=2 #protocol was a duplicate + break + fi + done < <(cat "/root/bin/protocols/master_protocol_list.txt" | grep "^${PROTOCOL_NAME^^}") + + if [ $FOUND -eq 0 ]; then #if no results found, let user know. + echo "❌ Protocol ${PROTOCOL_NAME^^} not found. Please try again." + fi + + read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"e\" to exit → " PROTOCOL_NAME + done +fi + +# Attempt to deploy application on start. + +if [ -z "$DEPLOY_ON_START" ]; then + read -p "🚀 Do you want to deploy your project automatically? (y/n) → " DEPLOY_ON_START +fi + +while [ "${DEPLOY_ON_START^^}" != "Y" ] && [ "${DEPLOY_ON_START^^}" != "N" ] && [ "${DEPLOY_ON_START^^}" != "" ]; do + echo "Please answer 'y' for yes or 'n' for no." + read -p "🚀 Do you want to deploy your project automatically? (y/n) → " DEPLOY_ON_START +done + +if [ "${GH_ACTION^^}" == "Y" ]; then + if [ ! -z "${RUNTIME_LANGUAGE^^}" ]; then + DEPLOY_ON_START="Y" + fi +fi + +if [ "${DEPLOY_ON_START^^}" == "Y" ]; then + source /root/bin/deploy-application.sh +fi + +# send public key, port mapping, env vars, and services to hypervisor + +send_file_to_hypervisor() { + local LOCAL_FILE="$1" + local REMOTE_FOLDER="$2" + if [ "$REMOTE_FOLDER" != "container-env-vars" ]; then + if [ -s "$LOCAL_FILE" ]; then + sftp root@10.15.0.4 < /dev/null +put $LOCAL_FILE /var/lib/vz/snippets/$REMOTE_FOLDER/ +EOF + fi + else + if [ -d "$LOCAL_FILE" ]; then + sftp root@10.15.0.4 < /dev/null +put -r $LOCAL_FILE /var/lib/vz/snippets/$REMOTE_FOLDER/ +EOF + else + ENV_FOLDER="null" + fi + fi +} + +send_file_to_hypervisor "$TEMP_PUB_FILE" "container-public-keys" +send_file_to_hypervisor "$PROTOCOL_FILE" "container-port-maps" +send_file_to_hypervisor "$ENV_FOLDER_PATH" "container-env-vars" +send_file_to_hypervisor "$TEMP_SERVICES_FILE_PATH" "container-services" + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}🚀 Starting Container Creation...${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +# Encode JSON variables +INSTALL_COMMAND_B64=$(echo -n "$INSTALL_COMMAND" | base64) +BUILD_COMMAND_B64=$(echo -n "$BUILD_COMMAND" | base64) +RUNTIME_LANGUAGE_B64=$(echo -n "$RUNTIME_LANGUAGE" | base64) +START_COMMAND_B64=$(echo -n "$START_COMMAND" | base64) +VERSIONS_DICT_B64=$(echo -n "$VERSIONS_DICT" | base64) + +REMOTE_CMD=( +/var/lib/vz/snippets/create-container.sh +"$CONTAINER_NAME" +"$GH_ACTION" +"$HTTP_PORT" +"$PROXMOX_USERNAME" +"$PUB_FILE" +"$PROTOCOL_BASE_FILE" +"$DEPLOY_ON_START" +"$PROJECT_REPOSITORY" +"$PROJECT_BRANCH" +"$PROJECT_ROOT" +"$INSTALL_COMMAND_B64" +"$BUILD_COMMAND_B64" +"$START_COMMAND_B64" +"$RUNTIME_LANGUAGE_B64" +"$ENV_FOLDER" +"$SERVICES_FILE" +"$LINUX_DISTRIBUTION" +"$MULTI_COMPONENT" +"$ROOT_START_COMMAND" +"$SELF_HOSTED_RUNNER" +"$VERSIONS_DICT_B64" +) + +QUOTED_REMOTE_CMD=$(printf ' %q' "${REMOTE_CMD[@]}") + +ssh -t root@10.15.0.4 "bash -c \"$QUOTED_REMOTE_CMD\"" + +rm -rf "$PROTOCOL_FILE" +rm -rf "$TEMP_PUB_FILE" +rm -rf "$TEMP_SERVICES_FILE_PATH" +rm -rf "$ENV_FOLDER_PATH" + +unset CONFIRM_PASSWORD +unset PUBLIC_KEY +unset PROXMOX_PASSWORD diff --git a/container-creation/container-creation/root-bin/deploy-application.sh b/container-creation/container-creation/root-bin/deploy-application.sh new file mode 100755 index 00000000..4d2d63f7 --- /dev/null +++ b/container-creation/container-creation/root-bin/deploy-application.sh @@ -0,0 +1,232 @@ +#!/bin/bash +# Helper script to gather project details for automatic deployment +# Modified August 5th, 2025 by Maxwell Klema +# ------------------------------------------ + +# Define color variables (works on both light and dark backgrounds) +RESET="\033[0m" +BOLD="\033[1m" +MAGENTA='\033[35m' + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}🌐 Let's Get Your Project Automatically Deployed ${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +writeLog "Starting deploy application script" + +# Get and validate project repository ======== + +if [ -z "$PROJECT_REPOSITORY" ]; then + read -p "🚀 Paste the link to your project repository → " PROJECT_REPOSITORY + writeLog "Prompted for project repository" +fi + +CheckRepository() { + PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY#*github.com/} + PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY_SHORTENED%.git} + REPOSITORY_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED) + writeLog "Checking repository existence for $PROJECT_REPOSITORY_SHORTENED" +} + +CheckRepository + +while [ "$REPOSITORY_EXISTS" != "200" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Repository Link. Make sure your repository is private." + writeLog "Invalid repository link entered: $PROJECT_REPOSITORY (GH_ACTION mode)" + exit 10 + fi + echo "⚠️ The repository link you provided, \"$PROJECT_REPOSITORY\" was not valid." + writeLog "Invalid repository link entered: $PROJECT_REPOSITORY" + read -p "🚀 Paste the link to your project repository → " PROJECT_REPOSITORY + CheckRepository +done + +writeLog "Repository validated: $PROJECT_REPOSITORY" + +# Get Repository Branch ======== + +if [ -z "$PROJECT_BRANCH" ]; then + read -p "🪾 Enter the project branch to deploy from (leave blank for \"main\") → " PROJECT_BRANCH + writeLog "Prompted for project branch" +fi + +if [ -z "$PROJECT_BRANCH" ]; then + PROJECT_BRANCH="main" + writeLog "Using default branch: main" +fi + +REPOSITORY_BRANCH_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED/tree/$PROJECT_BRANCH) +writeLog "Checking branch existence for $PROJECT_BRANCH" + +while [ "$REPOSITORY_BRANCH_EXISTS" != "200" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Branch. Make sure your branch exists on the repository." + writeLog "Invalid branch entered: $PROJECT_BRANCH (GH_ACTION mode)" + exit 11 + fi + echo "⚠️ The branch you provided, \"$PROJECT_BRANCH\", does not exist on repository at \"$PROJECT_REPOSITORY\"." + writeLog "Invalid branch entered: $PROJECT_BRANCH" + read -p "🪾 Enter the project branch to deploy from (leave blank for \"main\") → " PROJECT_BRANCH + if [ -z "$PROJECT_BRANCH" ]; then + PROJECT_BRANCH="main" + fi + REPOSITORY_BRANCH_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED/tree/$PROJECT_BRANCH) +done + +writeLog "Branch validated: $PROJECT_BRANCH" + +# Get Project Root Directory ======== + +if [ -z "$PROJECT_ROOT" ]; then + read -p "📁 Enter the project root directory (relative to repository root directory, or leave blank for root directory) → " PROJECT_ROOT + writeLog "Prompted for project root directory" +fi + +VALID_PROJECT_ROOT=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$PROJECT_ROOT") +writeLog "Validating project root directory: $PROJECT_ROOT" + +while [ "$VALID_PROJECT_ROOT" == "false" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Project Root Directory. Make sure your directory exists on the repository." + writeLog "Invalid project root directory entered: $PROJECT_ROOT (GH_ACTION mode)" + exit 12 + fi + echo "⚠️ The root directory you provided, \"$PROJECT_ROOT\", does not exist on branch, \"$PROJECT_BRANCH\", on repository at \"$PROJECT_REPOSITORY\"." + writeLog "Invalid project root directory entered: $PROJECT_ROOT" + read -p "📁 Enter the project root directory (relative to repository root directory, or leave blank for root directory) → " PROJECT_ROOT + VALID_PROJECT_ROOT=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$PROJECT_ROOT") +done + +writeLog "Project root directory validated: $PROJECT_ROOT" + +# Remove forward slash +if [[ "$PROJECT_ROOT" == "/*" ]]; then + PROJECT_ROOT="${PROJECT_ROOT:1}" +fi + +if [ "$PROJECT_ROOT" == "" ]; then + PROJECT_ROOT="." +fi + +# Check if the App has multiple components (backend, frontend, multiple servers, etc.) ======== + +if [ -z "$MULTI_COMPONENT" ]; then + read -p "🔗 Does your app consist of multiple components that run independently, i.e. seperate frontend and backend (y/n) → " MULTI_COMPONENT + writeLog "Prompted for multi-component option" +fi + +while [ "${MULTI_COMPONENT^^}" != "Y" ] && [ "${MULTI_COMPONENT^^}" != "N" ] && [ "${MULTI_COMPONENT^^}" != "" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid option for MULTI_COMPONENT. It must be 'y' or 'n'. Please try again." + writeLog "Invalid multi-component option entered: $MULTI_COMPONENT (GH_ACTION mode)" + exit 13 + fi + echo "⚠️ Invalid option. Please try again." + writeLog "Invalid multi-component option entered: $MULTI_COMPONENT" + read -p "🔗 Does your app consist of multiple components that run independently, i.e. seperate frontend and backend (y/n) → " MULTI_COMPONENT +done + +if [ "${GH_ACTION^^}" == "Y" ]; then + if [ ! -z "$RUNTIME_LANGUAGE" ] && echo "$RUNTIME_LANGUAGE" | jq . >/dev/null 2>&1; then # If RUNTIME_LANGUAGE is set and is valid JSON + MULTI_COMPONENT="Y" + fi +fi + +writeLog "Multi-component option set to: $MULTI_COMPONENT" + +# Gather Deployment Commands ======== + +# Helper functions to gather and validate component directory +gatherComponentDir() { + + COMPONENT_PATH="$2" + if [ -z "$COMPONENT_PATH" ]; then + read -p "$1, relative to project root directory (To Continue, Press Enter) → " COMPONENT_PATH + writeLog "Prompted for component directory: $1" + fi + # Check that component path is valid + VALID_COMPONENT_PATH=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$COMPONENT_PATH") + writeLog "Validating component path: $COMPONENT_PATH" + + while [ "$VALID_COMPONENT_PATH" == "false" ] && [ "$COMPONENT_PATH" != "" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Invalid Component Path: \"$COMPONENT_PATH\". Make sure your path exists on the repository." + writeLog "Invalid component path entered: $COMPONENT_PATH (GH_ACTION mode)" + exit 14 + fi + echo "⚠️ The component path you entered, \"$COMPONENT_PATH\", does not exist on branch, \"$PROJECT_BRANCH\", on repository at \"$PROJECT_REPOSITORY\"." + writeLog "Invalid component path entered: $COMPONENT_PATH" + if [ -z "$2" ]; then + read -p "$1, relative to project root directory (To Continue, Press Enter) → " COMPONENT_PATH + VALID_COMPONENT_PATH=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$COMPONENT_PATH") + else + exit 14 + fi + done + + if [[ "$COMPONENT_PATH" == /* ]]; then + COMPONENT_PATH="${COMPONENT_PATH:1}" # remove leading slash + fi + + if [ "$COMPONENT_PATH" != "" ]; then + writeLog "Component path validated: $COMPONENT_PATH" + fi +} + +UNIQUE_COMPONENTS=() + +# Helper function to add a component to unique components if its not already present +addComponent() { + COMPONENT="$1" + for CURRENT in "${UNIQUE_COMPONENTS[@]}"; do + if [ "${COMPONENT,,}" == "${CURRENT,,}" ]; then + return 0 + fi + done + UNIQUE_COMPONENTS+=("$COMPONENT") + writeLog "Added component: $COMPONENT" +} + +writeLog "Sourcing setup commands script" +source /root/bin/deployment-scripts/gatherSetupCommands.sh # Function to gather build, install, and start commands + +writeLog "Sourcing environment variables script" +source /root/bin/deployment-scripts/gatherEnvVars.sh # Gather Environment Variables + +writeLog "Gathering build commands" +gatherSetupCommands "BUILD" "🏗️ Enter the build command (leave blank if no build command) → " # Gather Build Command(s) + +if [ ! -z "$INSTALL_COMMAND" ] || [ -z "$ROOT_START_COMMAND" ]; then + writeLog "Gathering install commands" + gatherSetupCommands "INSTALL" "📦 Enter the install command (e.g., 'npm install') → " # Gather Install Command(s) +fi + +if [ ! -z "$START_COMMAND" ] || [ -z "$ROOT_START_COMMAND" ]; then + writeLog "Gathering start commands" + gatherSetupCommands "START" "🚦 Enter the start command (e.g., 'npm start', 'python app.py') → " # Gather Start Command(s) +fi + +if [ "${MULTI_COMPONENT^^}" == "Y" ]; then + if [ -z "$ROOT_START_COMMAND" ]; then + read -p "📍 If your container requires a start command at the root directory, i.e. Docker run, enter it here (leave blank for no command) → " ROOT_START_COMMAND + writeLog "Prompted for root start command" + fi + if [ "$ROOT_START_COMMAND" != "" ]; then + writeLog "Root start command set: $ROOT_START_COMMAND" + fi +fi + +# Get Runtime Language ======== + +if [ ! -z "$RUNTIME_LANGUAGE" ] || [ -z "$ROOT_START_COMMAND" ]; then + writeLog "Sourcing runtime languages script" + source /root/bin/deployment-scripts/gatherRuntimeLangs.sh +fi + +# Get Services ======== +writeLog "Sourcing services script" +source /root/bin/deployment-scripts/gatherServices.sh + +writeLog "Deployment process finished successfully" +echo -e "\n✅ Deployment Process Finished.\n" diff --git a/container-creation/container-creation/root-bin/deployment-scripts/gatherEnvVars.sh b/container-creation/container-creation/root-bin/deployment-scripts/gatherEnvVars.sh new file mode 100644 index 00000000..ad7e4827 --- /dev/null +++ b/container-creation/container-creation/root-bin/deployment-scripts/gatherEnvVars.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# Helper function to gather environment variables +gatherEnvVars(){ + TEMP_ENV_FILE_PATH="$1" + + read -p "🔑 Enter Environment Variable Key → " ENV_VAR_KEY + read -p "🔑 Enter Environment Variable Value → " ENV_VAR_VALUE + + while [ "$ENV_VAR_KEY" == "" ] || [ "$ENV_VAR_VALUE" == "" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Key and value cannot be empty. Please try again." + writeLog "Empty environment variable key or value entered (GH_ACTION mode)" + exit 15 + fi + echo "⚠️ Key or value cannot be empty. Try again." + writeLog "Empty environment variable key or value entered" + read -p "🔑 Enter Environment Variable Key → " ENV_VAR_KEY + read -p "🔑 Enter Environment Variable Value → " ENV_VAR_VALUE + done + + echo "$ENV_VAR_KEY=$ENV_VAR_VALUE" >> $TEMP_ENV_FILE_PATH + + read -p "🔑 Do you want to enter another Environment Variable? (y/n) → " ENTER_ANOTHER_ENV + + while [ "${ENTER_ANOTHER_ENV^^}" == "Y" ]; do + gatherEnvVars "$TEMP_ENV_FILE_PATH" + done +} + +if [ -z "$REQUIRE_ENV_VARS" ]; then + read -p "🔑 Does your application require environment variables? (y/n) → " REQUIRE_ENV_VARS +fi + +while [ "${REQUIRE_ENV_VARS^^}" != "Y" ] && [ "${REQUIRE_ENV_VARS^^}" != "N" ] && [ "${REQUIRE_ENV_VARS^^}" != "" ]; do + echo "⚠️ Invalid option. Please try again." + writeLog "Invalid environment variables requirement option entered: $REQUIRE_ENV_VARS" + read -p "🔑 Does your application require environment variables? (y/n) → " REQUIRE_ENV_VARS +done + +if [ "${GH_ACTION^^}" == "Y" ]; then + if [ ! -z "$CONTAINER_ENV_VARS" ]; then + REQUIRE_ENV_VARS="Y" + fi +fi + +if [ "${REQUIRE_ENV_VARS^^}" == "Y" ]; then + # generate random temp .env folder to store all env files for different components + RANDOM_NUM=$(shuf -i 100000-999999 -n 1) + ENV_FOLDER="env_$RANDOM_NUM" + ENV_FOLDER_PATH="/root/bin/env/$ENV_FOLDER" + mkdir -p "$ENV_FOLDER_PATH" + + if [ "${MULTI_COMPONENT^^}" == "Y" ]; then + if [ ! -z "$CONTAINER_ENV_VARS" ]; then # Environment Variables + if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success + for key in $(echo "$CONTAINER_ENV_VARS" | jq -r 'keys[]'); do + gatherComponentDir "Enter the path of your component to enter environment variables" "$key" + ENV_FILE_NAME=$(echo "$COMPONENT_PATH" | tr '/' '_') + ENV_FILE_NAME="$ENV_FILE_NAME.txt" + ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE_NAME" + touch "$ENV_FILE_PATH" + echo "$CONTAINER_ENV_VARS" | jq -r --arg key "$key" '.[$key] | to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" + addComponent "$key" + done + else + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS (GH_ACTION mode)" + exit 16 + fi + echo "⚠️ Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS" + exit 16 + fi + else # No Environment Variables + gatherComponentDir "Enter the path of your component to enter environment variables" + + while [ "$COMPONENT_PATH" != "" ]; do + addComponent "$COMPONENT_PATH" + ENV_FILE_NAME=$(echo "$COMPONENT_PATH" | tr '/' '_') + ENV_FILE="$ENV_FILE_NAME.txt" + ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE" + touch "$ENV_FILE_PATH" + gatherEnvVars "$ENV_FILE_PATH" + gatherComponentDir "Enter the path of your component to enter environment variables" + done + fi + else # Single Component + ENV_FILE="env_$RANDOM_NUM.txt" + ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE" + touch "$ENV_FILE_PATH" + + if [ ! -z "$CONTAINER_ENV_VARS" ]; then # Environment Variables + if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success + echo "$CONTAINER_ENV_VARS " | jq -r 'to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" #k=v pairs + else + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS for single component (GH_ACTION mode)" + exit 16 + fi + echo "⚠️ Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS for single component" + exit 16 + fi + else # No Environment Variables + gatherEnvVars "$ENV_FILE_PATH" + fi + fi +fi diff --git a/container-creation/container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh b/container-creation/container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh new file mode 100755 index 00000000..f78c5151 --- /dev/null +++ b/container-creation/container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +# Keep track of the runtime languages and versions for each component +VERSIONS_DICT={} + +gatherRunTime() { + COMPONENT_PATH="$1" + + if [ -z "${RUNTIME_LANGUAGE}" ] || [ "$RT_ENV_VAR" != "true" ]; then + read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE + fi + + RUNTIME_LANGUAGE_REGEX="^(python|nodejs)(@([0-9]+(\.[0-9]+){0,2}))?$" + + while [[ ! ${RUNTIME_LANGUAGE,,} =~ $RUNTIME_LANGUAGE_REGEX ]]; do + echo "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." + writeLog "Unsupported runtime environment entered: $RUNTIME_LANGUAGE for component: $COMPONENT_PATH" + if [ "${GH_ACTION^^}" == "Y" ]; then + exit 17 + fi + read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE + done + + # Scrape runtime version + if [[ "${RUNTIME_LANGUAGE,,}" == *"@"* ]]; then + if [ "${MULTI_COMPONENT^^}" == "Y" ]; then + RUNTIME_VERSION=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/.*@([0-9]+(\.[0-9]+){0,2}).*/\1/') + UPDATED_VERSIONS_DICT=$(echo "$VERSIONS_DICT" | jq --arg k "$COMPONENT_PATH" --arg v "$RUNTIME_VERSION" '. + {($k): $v}') + VERSIONS_DICT=$UPDATED_VERSIONS_DICT + else + RUNTIME_VERSION=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/.*@([0-9]+(\.[0-9]+){0,2}).*/\1/') + UPDATED_VERSIONS_DICT=$(echo "$VERSIONS_DICT" | jq --arg k "default" --arg v "$RUNTIME_VERSION" '. + {($k): $v}') + VERSIONS_DICT=$UPDATED_VERSIONS_DICT + fi + fi + RUNTIME_LANGUAGE=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/@.*//') +} + +# Helper function to remove an item from a list +removeFromList() { + ITEM_TO_REMOVE="$1" + NEW_LIST=() + for ITEM in "${UNIQUE_COMPONENTS_CLONE[@]}"; do + if [ "$ITEM" != "$ITEM_TO_REMOVE" ]; then + NEW_LIST+=("$ITEM") + fi + done + UNIQUE_COMPONENTS_CLONE=("${NEW_LIST[@]}") +} + +UNIQUE_COMPONENTS_CLONE=("${UNIQUE_COMPONENTS[@]}") +RUNTIME_LANGUAGE_DICT={} + + +if [ "${MULTI_COMPONENT^^}" == 'Y' ]; then + if [ ! -z "$RUNTIME_LANGUAGE" ]; then # Environment Variable Passed + if echo "$RUNTIME_LANGUAGE" | jq -e > /dev/null 2>&1; then # Valid JSON + for key in $(echo "$RUNTIME_LANGUAGE" | jq -r 'keys[]'); do + RUNTIME_LANGUAGE_REGEX="^(python|nodejs)(@([0-9]+(\.[0-9]+){0,2}))?$" + COMPONENT_RUNTIME_LANGUAGE=$(echo "$RUNTIME_LANGUAGE" | jq -r --arg key "$key" '.[$key]') + while [[ ! ${COMPONENT_RUNTIME_LANGUAGE,,} =~ $RUNTIME_LANGUAGE_REGEX ]]; do + outputError "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." + writeLog "Unsupported runtime environment entered: $COMPONENT_RUNTIME_LANGUAGE for component: $COMPONENT_PATH (GH_ACTION mode)" + if [ "${GH_ACTION^^}" == "Y" ]; then + exit 17 + fi + read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " COMPONENT_RUNTIME_LANGUAGE + done + + # Scrape runtime version + if [[ "${COMPONENT_RUNTIME_LANGUAGE,,}" == *"@"* ]]; then + RUNTIME_VERSION=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/.*@([0-9]+(\.[0-9]+){0,2}).*/\1/') + UPDATED_VERSIONS_DICT=$(echo "$VERSIONS_DICT" | jq --arg k "$key" --arg v "$RUNTIME_VERSION" '. + {($k): $v}') + VERSIONS_DICT=$UPDATED_VERSIONS_DICT + fi + COMPONENT_RUNTIME_LANGUAGE=$(echo "${COMPONENT_RUNTIME_LANGUAGE,,}" | sed -E 's/@.*//') + RUNTIME_LANGUAGE=$(echo "$RUNTIME_LANGUAGE" \ + | jq -c --arg k "$key" --arg v "$COMPONENT_RUNTIME_LANGUAGE" '.[$k] = $v') + + removeFromList "$key" + + done + if [ ${#UNIQUE_COMPONENTS_CLONE[@]} -gt 0 ]; then #if there are still components in the list, then not all runtimes were provided, so exit on error + echo "⚠️ You did not provide runtime languages for these components: \"${UNIQUE_COMPONENTS_CLONE[@]}\"." + writeLog "Missing runtime languages for components: ${UNIQUE_COMPONENTS_CLONE[@]}" + exit 18 + fi + else + echo "⚠️ Your \"$RUNTIME_LANGUAGE\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in RUNTIME_LANGUAGE" + exit 16 + fi + else # No Environment Variable Passed + for CURRENT in "${UNIQUE_COMPONENTS[@]}"; do + gatherRunTime "$CURRENT" + RUNTIME_LANGUAGE_DICT=$(echo "$RUNTIME_LANGUAGE_DICT" | jq --arg k "$CURRENT" --arg v "$RUNTIME_LANGUAGE" '. + {($k): $v}') + done + RUNTIME_LANGUAGE=$RUNTIME_LANGUAGE_DICT + fi +else + if [ ! -z "$RUNTIME_LANGUAGE" ]; then + RT_ENV_VAR="true" + fi + gatherRunTime "$PROJECT_REPOSITORY" +fi diff --git a/container-creation/container-creation/root-bin/deployment-scripts/gatherServices.sh b/container-creation/container-creation/root-bin/deployment-scripts/gatherServices.sh new file mode 100755 index 00000000..1cc06c39 --- /dev/null +++ b/container-creation/container-creation/root-bin/deployment-scripts/gatherServices.sh @@ -0,0 +1,158 @@ +SERVICE_MAP="/root/bin/services/service_map_$LINUX_DISTRIBUTION.json" +APPENDED_SERVICES=() + +# Helper function to check if a user has added the same service twice +serviceExists() { + SERVICE="$1" + for CURRENT in "${APPENDED_SERVICES[@]}"; do + if [ "${SERVICE,,}" == "${CURRENT,,}" ]; then + return 0 + fi + done + return 1 +} + +processService() { + local SERVICE="$1" + local MODE="$2" # "batch" or "single" + + SERVICE_IN_MAP=$(jq -r --arg key "${SERVICE,,}" '.[$key] // empty' "$SERVICE_MAP") + if serviceExists "$SERVICE"; then + if [ "$MODE" = "batch" ]; then + return 0 # skip to next in batch mode + else + echo "⚠️ You already added \"$SERVICE\" as a service. Please try again." + writeLog "Duplicate service attempted: $SERVICE" + return 0 + fi + elif [ "${SERVICE^^}" != "C" ] && [ "${SERVICE^^}" != "" ] && [ -n "$SERVICE_IN_MAP" ]; then + jq -r --arg key "${SERVICE,,}" '.[$key][]' "$SERVICE_MAP" >> "$TEMP_SERVICES_FILE_PATH" + echo "sudo systemctl daemon-reload" >> "$TEMP_SERVICES_FILE_PATH" + echo "✅ ${SERVICE^^} added to your container." + APPENDED_SERVICES+=("${SERVICE^^}") + elif [ "${SERVICE^^}" == "C" ]; then + appendCustomService + elif [ "${SERVICE^^}" != "" ]; then + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "⚠️ Service \"$SERVICE\" does not exist." + writeLog "Invalid service entered: $SERVICE (GH_ACTION mode)" + exit 20 + fi + echo "⚠️ Service \"$SERVICE\" does not exist." + writeLog "Invalid service entered: $SERVICE" + [ "$MODE" = "batch" ] && exit 20 + fi +} + +# Helper function to append a new service to a container +appendService() { + if [ ! -z "$SERVICES" ]; then + for SERVICE in $(echo "$SERVICES" | jq -r '.[]'); do + processService "$SERVICE" "batch" + done + else + read -p "➡️ Enter the name of a service to add to your container or type \"C\" to set up a custom service installation (Enter to exit) → " SERVICE + processService "$SERVICE" "single" + fi +} + +appendCustomService() { + # If there is an env variable for custom services, iterate through each command and append it to temporary services file + if [ ! -z "$CUSTOM_SERVICES" ]; then + echo "$CUSTOM_SERVICES" | jq -c -r '.[]' | while read -r CUSTOM_SERVICE; do + echo "$CUSTOM_SERVICE" | jq -c -r '.[]' | while read -r CUSTOM_SERVICE_COMMAND; do + if [ ! -z "$CUSTOM_SERVICE_COMMAND" ]; then + echo "$CUSTOM_SERVICE_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" + else + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "⚠️ Custom Service Installation Command cannot be empty in \"$CUSTOM_SERVICE\"." + writeLog "Empty custom service command in: $CUSTOM_SERVICE (GH_ACTION mode)" + exit 21 + fi + echo "⚠️ Command cannot be empty." + writeLog "Empty custom service command in: $CUSTOM_SERVICE" + exit 21; + fi + done + done + echo "✅ Custom Services appended." + else + echo "🛎️ Configuring Custom Service Installation. For each prompt, enter a command that is a part of the installation process for your service on Debian Bookworm. Do not forget to enable and start the service at the end. Once you have entered all of your commands, press enter to continue" + COMMAND_NUM=1 + read -p "➡️ Enter Command $COMMAND_NUM: " CUSTOM_COMMAND + + echo "$CUSTOM_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" + + while [ "${CUSTOM_COMMAND^^}" != "" ]; do + ((COMMAND_NUM++)) + read -p "➡️ Enter Command $COMMAND_NUM: " CUSTOM_COMMAND + echo "$CUSTOM_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" + done + fi +} + +# Helper function to see if a user wants to set up a custom service +setUpService() { + read -p "🛎️ Do you wish to set up a custom service installation? (y/n) " SETUP_CUSTOM_SERVICE_INSTALLATION + while [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "Y" ] && [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "N" ] && [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "" ]; do + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "⚠️ Invalid custom service installation option. Please try again." + writeLog "Invalid custom service installation option entered: $SETUP_CUSTOM_SERVICE_INSTALLATION (GH_ACTION mode)" + exit 22 + fi + echo "⚠️ Invalid option. Please try again." + writeLog "Invalid custom service installation option entered: $SETUP_CUSTOM_SERVICE_INSTALLATION" + read -p "🛎️ Do you wish to set up a custom service installation? (y/n) " SETUP_CUSTOM_SERVICE_INSTALLATION + done +} + +if [ -z "$REQUIRE_SERVICES" ]; then + read -p "🛎️ Does your application require special services (i.e. Docker, MongoDB, etc.) to run on the container? (y/n) → " REQUIRE_SERVICES +fi + +while [ "${REQUIRE_SERVICES^^}" != "Y" ] && [ "${REQUIRE_SERVICES^^}" != "N" ] && [ "${REQUIRE_SERVICES^^}" != "" ]; do + echo "⚠️ Invalid option. Please try again." + writeLog "Invalid service requirement option entered: $REQUIRE_SERVICES" + read -p "🛎️ Does your application require special services (i.e. Docker, MongoDB, etc.) to run on the container? (y/n) → " REQUIRE_SERVICES +done + +if [ "${GH_ACTION^^}" == "Y" ]; then + if [ ! -z "$SERVICES" ] || [ ! -z "$CUSTOM_SERVICES" ]; then + REQUIRE_SERVICES="Y" + fi +fi + +if [ "${REQUIRE_SERVICES^^}" == "Y" ]; then + + # Generate random (temporary) file to store install commands for needed services + RANDOM_NUM=$(shuf -i 100000-999999 -n 1) + SERVICES_FILE="services_$RANDOM_NUM.txt" + TEMP_SERVICES_FILE_PATH="/root/bin/services/$SERVICES_FILE" + touch "$TEMP_SERVICES_FILE_PATH" + + appendService + while [ "${SERVICE^^}" != "" ] || [ ! -z "$SERVICES" ]; do + if [ -z "$SERVICES" ]; then + appendService + else + if [ ! -z "$CUSTOM_SERVICES" ]; then # assumes both services and custom services passed as ENV vars + appendCustomService + else # custom services not passed as ENV var, so must prompt the user for their custom services + setUpService + while [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" == "Y" ]; do + appendCustomService + setUpService + done + fi + break + fi + done +fi + +# Used for updating container services in GH Actions + +UPDATING_CONTAINER="$1" +if [ "$UPDATING_CONTAINER" == "true" ]; then + cat "$TEMP_SERVICES_FILE_PATH" + rm -rf "$TEMP_SERVICES_FILE_PATH" +fi \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh b/container-creation/container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh new file mode 100644 index 00000000..6ff40ecb --- /dev/null +++ b/container-creation/container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# This function gathers start up commands, such as build, install, and start, for both single and multiple component applications +# Last Modified by Maxwell Klema on July 15th, 2025 +# --------------------------------------------- + +gatherSetupCommands() { + + TYPE="$1" + PROMPT="$2" + TYPE_COMMAND="${TYPE}_COMMAND" + TYPE_COMMAND="${!TYPE_COMMAND}" # get value stored by TYPE_COMMAND + declare "COMMANDS_DICT={}" + + if [ "${MULTI_COMPONENT^^}" == "Y" ]; then + if [ ! -z "$TYPE_COMMAND" ]; then # Environment Variable Passed + if echo "$TYPE_COMMAND" | jq -e > /dev/null 2>&1; then # Valid JSON + for key in $(echo "$TYPE_COMMAND" | jq -r 'keys[]'); do + gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" "$key" + addComponent "$key" + done + else + if [ "${GH_ACTION^^}" == "Y" ]; then + outputError "Your \"$TYPE_COMMAND\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in $TYPE_COMMAND (GH_ACTION mode)" + exit 10 + fi + echo "⚠️ Your \"$TYPE_COMMAND\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in $TYPE_COMMAND" + exit 10 + fi + else # No Environment Variable Passed + gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" + while [ "$COMPONENT_PATH" != "" ]; do + addComponent "$COMPONENT_PATH" + read -p "$PROMPT" COMMAND + + # Append Component:Command k:v pair to map + COMMANDS_DICT=$(echo "$COMMANDS_DICT" | jq --arg k "$COMPONENT_PATH" --arg v "$COMMAND" '. + {($k): $v}') + gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" + done + TYPE_COMMAND=$COMMANDS_DICT + fi + else + if [ -z "$TYPE_COMMAND" ]; then + read -p "$PROMPT" TYPE_COMMAND + fi + fi + + # Write to correct command variable + if [ "$TYPE" == "BUILD" ]; then + BUILD_COMMAND=$TYPE_COMMAND + elif [ "$TYPE" == "INSTALL" ]; then + INSTALL_COMMAND=$TYPE_COMMAND + else + START_COMMAND=$TYPE_COMMAND + fi +} \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/js/authenticateRepo.js b/container-creation/container-creation/root-bin/js/authenticateRepo.js new file mode 100644 index 00000000..37a57e7a --- /dev/null +++ b/container-creation/container-creation/root-bin/js/authenticateRepo.js @@ -0,0 +1,23 @@ +const axios = require('axios'); + +function authenticateRepo(repositoryURL, branch, folderPath) { + + if (folderPath.indexOf('.') != -1) { + return Promise.resolve(false); //early exit if path points to a specific file + } + + repositoryURL = repositoryURL.replace('.git', ''); + if (folderPath.startsWith('/')) { + folderPath = folderPath.substring(1, folderPath.length); + } + fullURL = `${repositoryURL}/tree/${branch}/${folderPath}` + + const config = { + method: "get", + url: fullURL + } + + return axios.request(config).then((response) => response.status === 200).catch(() => false); +} + +module.exports = { authenticateRepo } \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/js/authenticateUser.js b/container-creation/container-creation/root-bin/js/authenticateUser.js new file mode 100644 index 00000000..36942c4f --- /dev/null +++ b/container-creation/container-creation/root-bin/js/authenticateUser.js @@ -0,0 +1,28 @@ +const axios = require('axios'); +const qs = require('qs'); +const https = require('https'); + +// authenticates user, ensuring they have a valid proxmox account +function authenticateUser(username, password) { + let data = qs.stringify({ + 'username': username + "@pve", + 'password': password + }) + + let config = { + method: 'post', + url: ' https://10.15.0.4:8006/api2/json/access/ticket', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false // Disable SSL verification for self-signed certificates (Only because public facing domain is resolved to nginx server internally, so have to use hypervisor IP instead of domain) + }), + data: data + }; + + return axios.request(config).then((response) => response.status === 200).catch(() => false); +} + + +module.exports = { authenticateUser }; diff --git a/container-creation/container-creation/root-bin/js/package-lock.json b/container-creation/container-creation/root-bin/js/package-lock.json new file mode 100644 index 00000000..43d24316 --- /dev/null +++ b/container-creation/container-creation/root-bin/js/package-lock.json @@ -0,0 +1,383 @@ +{ + "name": "js", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.10.0", + "https": "^1.0.0", + "qs": "^6.14.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.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==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "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==", + "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==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", + "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + }, + "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==", + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "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==", + "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==", + "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==", + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + } + } +} diff --git a/container-creation/container-creation/root-bin/js/package.json b/container-creation/container-creation/root-bin/js/package.json new file mode 100644 index 00000000..64c6916b --- /dev/null +++ b/container-creation/container-creation/root-bin/js/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "axios": "^1.10.0", + "https": "^1.0.0", + "qs": "^6.14.0" + } +} \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/js/runner.js b/container-creation/container-creation/root-bin/js/runner.js new file mode 100644 index 00000000..8fde30cb --- /dev/null +++ b/container-creation/container-creation/root-bin/js/runner.js @@ -0,0 +1,13 @@ +authenticateuser = require("./authenticateUser.js"); +authenticaterepo = require("./authenticateRepo.js") + +const [, , func, ...args] = process.argv; +if (func == "authenticateUser") { + authenticateuser.authenticateUser(...args).then((result) => { + console.log(result); + }); +} else if (func == "authenticateRepo") { + authenticaterepo.authenticateRepo(...args).then((result) => { + console.log(result); + }) +} diff --git a/container-creation/container-creation/root-bin/opensource-server b/container-creation/container-creation/root-bin/opensource-server new file mode 120000 index 00000000..a8a4f8c2 --- /dev/null +++ b/container-creation/container-creation/root-bin/opensource-server @@ -0,0 +1 @@ +../../.. \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/protocols/master_protocol_list.txt b/container-creation/container-creation/root-bin/protocols/master_protocol_list.txt new file mode 100644 index 00000000..ba1d16e4 --- /dev/null +++ b/container-creation/container-creation/root-bin/protocols/master_protocol_list.txt @@ -0,0 +1,145 @@ +TCPM 1 tcp +RJE 5 tcp +ECHO 7 tcp +DISCARD 9 tcp +DAYTIME 13 tcp +QOTD 17 tcp +MSP 18 tcp +CHARGEN 19 tcp +FTP 20 tcp +FTP 21 tcp +SSH 22 tcp +TELNET 23 tcp +SMTP 25 tcp +TIME 37 tcp +HNS 42 tcp +WHOIS 43 tcp +TACACS 49 tcp +DNS 53 tcp +BOOTPS 67 udp +BOOTPC 68 udp +TFTP 69 udp +GOPHER 70 tcp +FINGER 79 tcp +HTTP 80 tcp +KERBEROS 88 tcp +HNS 101 tcp +ISO-TSAP 102 tcp +POP2 109 tcp +POP3 110 tcp +RPC 111 tcp +AUTH 113 tcp +SFTP 115 tcp +UUCP-PATH 117 tcp +NNTP 119 tcp +NTP 123 udp +EPMAP 135 tcp +NETBIOS-NS 137 tcp +NETBIOS-DGM 138 udp +NETBIOS-SSN 139 tcp +IMAP 143 tcp +SQL-SRV 156 tcp +SNMP 161 udp +SNMPTRAP 162 udp +XDMCP 177 tcp +BGP 179 tcp +IRC 194 tcp +LDAP 389 tcp +NIP 396 tcp +HTTPS 443 tcp +SNPP 444 tcp +SMB 445 tcp +KPASSWD 464 tcp +SMTPS 465 tcp +ISAKMP 500 udp +EXEC 512 tcp +LOGIN 513 tcp +SYSLOG 514 udp +LPD 515 tcp +TALK 517 udp +NTALK 518 udp +RIP 520 udp +RIPNG 521 udp +RPC 530 tcp +UUCP 540 tcp +KLOGIN 543 tcp +KSHELL 544 tcp +DHCPV6-C 546 tcp +DHCPV6-S 547 tcp +AFP 548 tcp +RTSP 554 tcp +NNTPS 563 tcp +SUBMISSION 587 tcp +IPP 631 tcp +LDAPS 636 tcp +LDP 646 tcp +LINUX-HA 694 tcp +ISCSI 860 tcp +RSYNC 873 tcp +VMWARE 902 tcp +FTPS-DATA 989 tcp +FTPS 990 tcp +TELNETS 992 tcp +IMAPS 993 tcp +POP3S 995 tcp +SOCKS 1080 tcp +OPENVPN 1194 udp +OMGR 1311 tcp +MS-SQL-S 1433 tcp +MS-SQL-M 1434 udp +WINS 1512 tcp +ORACLE-SQL 1521 tcp +RADIUS 1645 tcp +RADIUS-ACCT 1646 tcp +L2TP 1701 udp +PPTP 1723 tcp +CISCO-ISL 1741 tcp +RADIUS 1812 udp +RADIUS-ACCT 1813 udp +NFS 2049 tcp +CPANEL 2082 tcp +CPANEL-SSL 2083 tcp +WHM 2086 tcp +WHM-SSL 2087 tcp +DA 2222 tcp +ORACLE-DB 2483 tcp +ORACLE-DBS 2484 tcp +XBOX 3074 tcp +HTTP-PROXY 3128 tcp +MYSQL 3306 tcp +RDP 3389 tcp +NDPS-PA 3396 tcp +SVN 3690 tcp +MSQL 4333 udp +METASPLOIT 4444 tcp +EMULE 4662 tcp +EMULE 4672 udp +RADMIN 4899 tcp +UPNP 5000 tcp +YMSG 5050 tcp +SIP 5060 tcp +SIP-TLS 5061 tcp +AIM 5190 tcp +XMPP-CLIENT 5222 tcp +XMPP-CLIENTS 5223 tcp +XMPP-SERVER 5269 tcp +POSTGRES 5432 tcp +VNC 5500 tcp +VNC-HTTP 5800 tcp +VNC 5900 tcp +X11 6000 tcp +BNET 6112 tcp +GNUTELLA 6346 tcp +SANE 6566 tcp +IRC 6667 tcp +IRCS 6697 tcp +BT 6881 tcp +HTTP-ALT 8000 tcp +HTTP-ALT 8008 tcp +HTTP-ALT 8080 tcp +HTTPS-ALT 8443 tcp +PDL-DS 9100 tcp +BACNET 9101 tcp +WEBMIN 10000 udp +MONGO 27017 tcp +TRACEROUTE 33434 udp \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/services/service_map_debian.json b/container-creation/container-creation/root-bin/services/service_map_debian.json new file mode 100644 index 00000000..e490361d --- /dev/null +++ b/container-creation/container-creation/root-bin/services/service_map_debian.json @@ -0,0 +1,67 @@ +{ + "meteor": ["curl https://install.meteor.com/ | sh"], + "mongodb": [ + "sudo apt update -y", + "sudo apt install -y gnupg curl", + "curl -fsSL https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-7.0.gpg", + "echo \"deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 main\" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list", + "sudo apt update -y", + "sudo apt install -y mongodb-org", + "sudo systemctl enable mongod", + "sudo systemctl start mongod" + ], + "redis": [ + "sudo apt update -y", + "sudo apt install -y redis-server", + "sudo systemctl enable redis-server", + "sudo systemctl start redis-server" + ], + "postgresql": [ + "sudo apt update -y", + "sudo apt install -y postgresql postgresql-contrib", + "sudo systemctl enable postgresql", + "sudo systemctl start postgresql" + ], + "apache": [ + "sudo apt update -y", + "sudo apt install -y apache2", + "sudo systemctl enable apache2", + "sudo systemctl start apache2" + ], + "nginx": [ + "sudo apt update -y", + "sudo apt install -y nginx", + "sudo systemctl enable nginx", + "sudo systemctl start nginx" + ], + "docker": [ + "sudo apt update -y", + "sudo apt install -y lsb-release", + "sudo apt install -y ca-certificates curl gnupg lsb-release", + "sudo install -m 0755 -d /etc/apt/keyrings", + "curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg", + "echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable' | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", + "sudo apt update -y", + "sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin", + "sudo systemctl enable docker", + "sudo systemctl start docker" + ], + "rabbitmq": [ + "sudo apt update -y", + "sudo apt install -y rabbitmq-server", + "sudo systemctl enable rabbitmq-server", + "sudo systemctl start rabbitmq-server" + ], + "memcached": [ + "sudo apt update -y", + "sudo apt install -y memcached", + "sudo systemctl enable memcached", + "sudo systemctl start memcached" + ], + "mariadb": [ + "sudo apt update -y", + "sudo apt install -y mariadb-server", + "sudo systemctl enable mariadb", + "sudo systemctl start mariadb" + ] +} diff --git a/container-creation/container-creation/root-bin/services/service_map_rocky.json b/container-creation/container-creation/root-bin/services/service_map_rocky.json new file mode 100644 index 00000000..7b7cda85 --- /dev/null +++ b/container-creation/container-creation/root-bin/services/service_map_rocky.json @@ -0,0 +1,96 @@ +{ + "meteor": ["dnf install tar -y", "curl https://install.meteor.com/ | sh"], + "mongodb": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y gnupg curl", + "curl -fsSL https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor -o /etc/pki/rpm-gpg/RPM-GPG-KEY-mongodb", + "echo '[mongodb-org-7.0]' | sudo tee /etc/yum.repos.d/mongodb-org-7.0.repo", + "echo 'name=MongoDB Repository' >> /etc/yum.repos.d/mongodb-org-7.0.repo", + "echo 'baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/7.0/x86_64/' >> /etc/yum.repos.d/mongodb-org-7.0.repo", + "echo 'gpgcheck=1' >> /etc/yum.repos.d/mongodb-org-7.0.repo", + "echo 'enabled=1' >> /etc/yum.repos.d/mongodb-org-7.0.repo", + "echo 'gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mongodb' >> /etc/yum.repos.d/mongodb-org-7.0.repo", + "sudo dnf install -y mongodb-org", + "sudo systemctl enable mongod", + "sudo systemctl start mongod" + ], + "redis": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y redis", + "sudo systemctl enable redis", + "sudo systemctl start redis" + ], + "postgresql": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y postgresql-server postgresql-contrib", + "sudo postgresql-setup --initdb", + "sudo systemctl enable postgresql", + "sudo systemctl start postgresql" + ], + "apache": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y httpd", + "sudo systemctl enable httpd", + "sudo systemctl start httpd" + ], + "httpd": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y httpd", + "sudo systemctl enable httpd", + "sudo systemctl start httpd" + ], + "nginx": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y nginx", + "sudo systemctl enable nginx", + "sudo systemctl start nginx" + ], + "docker": [ + "sudo dnf update -y", + "sudo dnf install -y yum-utils device-mapper-persistent-data lvm2", + "sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo", + "sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin", + "sudo systemctl enable docker", + "sudo systemctl start docker" + ], + "rabbitmq": [ + "sudo dnf install -y epel-release", + "sudo dnf install -y erlang", + "sudo dnf update -y", + "rpm --import 'https://github.com/rabbitmq/signing-keys/releases/download/3.0/rabbitmq-release-signing-key.asc'", + "rpm --import 'https://github.com/rabbitmq/signing-keys/releases/download/3.0/cloudsmith.rabbitmq-server.9F4587F226208342.key'", + "echo '[rabbitmq-server]' | sudo tee /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'name=rabbitmq-server' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'baseurl=https://packagecloud.io/rabbitmq/rabbitmq-server/el/9/$basearch' | sudo tee /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'repo_gpgcheck=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'gpgcheck=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'enabled=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'gpgkey=https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'sslverify=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'sslcacert=/etc/pki/tls/certs/ca-bundle.crt' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "echo 'metadata_expire=300' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", + "sudo dnf install -y rabbitmq-server", + "sudo systemctl enable rabbitmq-server", + "sudo systemctl start rabbitmq-server" + ], + "memcached": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y memcached", + "sudo systemctl enable memcached", + "sudo systemctl start memcached" + ], + "mariadb": [ + "sudo dnf install -y epel-release", + "sudo dnf update -y", + "sudo dnf install -y mariadb-server", + "sudo systemctl enable mariadb", + "sudo systemctl start mariadb" + ] +} diff --git a/container-creation/container-creation/root-bin/ssh/detectPublicKey.sh b/container-creation/container-creation/root-bin/ssh/detectPublicKey.sh new file mode 100755 index 00000000..e9512e3c --- /dev/null +++ b/container-creation/container-creation/root-bin/ssh/detectPublicKey.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Detect if the user in the current session logged in via an SSH public key +# Last Updated June 17th 2025 Maxwell Klema + +USER="create-container" #Change Later +PUBLIC_KEY_LIST="/root/.ssh/authorized_keys" + +KEY_FINGERPRINT="$1" +TEMP_PUB_FILE="$2" + +if [ "$KEY_FINGERPRINT" != "" ]; then + # Iterate over each public key, compute fingerprint, see if there is a match + while read line; do + echo "$line" > "$TEMP_PUB_FILE" + PUB_FINGERPRINT=$(ssh-keygen -lf "$TEMP_PUB_FILE" 2>/dev/null | awk '{print $2}') + if [[ "$PUB_FINGERPRINT" == "$KEY_FINGERPRINT" ]]; then + echo "Public key found for $USER" + exit 0 + fi + done < <(tac $PUBLIC_KEY_LIST) #Iterates backwards without creating subprocess (allows exit in loop) + + echo "" > "$TEMP_PUB_FILE" +fi \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/ssh/publicKeyAppendJumpHost.sh b/container-creation/container-creation/root-bin/ssh/publicKeyAppendJumpHost.sh new file mode 100755 index 00000000..5e5c7012 --- /dev/null +++ b/container-creation/container-creation/root-bin/ssh/publicKeyAppendJumpHost.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# A script that appends a user's public key to the SSH jump host container to prevent them having to enter a password +# June 24th, 2025 Maxwell Klema + +PUBLIC_KEY=$1 +JUMP_HOST="10.15.0.4" #temporary until jump server ready + +# SSH into the Jump Host + +ssh root@"$JUMP_HOST" "echo '$PUBLIC_KEY' >> /home/create-container/.ssh/authorized_keys && systemctl restart ssh" + +exit 0 diff --git a/container-creation/container-creation/root-bin/test.sh b/container-creation/container-creation/root-bin/test.sh new file mode 100755 index 00000000..abfad803 --- /dev/null +++ b/container-creation/container-creation/root-bin/test.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# VERSIONS_DICT={} + +# gatherRunTime() { +# COMPONENT_PATH="$1" + +# if [ -z "${RUNTIME_LANGUAGE}" ] || [ "$RT_ENV_VAR" != "true" ]; then +# read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE +# fi + +# RUNTIME_LANGUAGE_REGEX="^(python|nodejs)(@([0-9]+(\.[0-9]+){0,2}))?$" + +# while [[ ! ${RUNTIME_LANGUAGE,,} =~ $RUNTIME_LANGUAGE_REGEX ]]; do +# echo "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." +# # writeLog "Unsupported runtime environment entered: $RUNTIME_LANGUAGE for component: $COMPONENT_PATH" +# if [ "${GH_ACTION^^}" == "Y" ]; then +# outputError "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." +# # writeLog "Unsupported runtime environment entered: $RUNTIME_LANGUAGE (GH_ACTION mode)" +# exit 17 +# fi +# read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE +# done + +# # Scrape runtime version +# if [[ "${RUNTIME_LANGUAGE,,}" == *"@"* ]]; then +# if [ "${MULTI_COMPONENT^^}" == "Y" ]; then +# RUNTIME_VERSION=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/.*@([0-9]+(\.[0-9]+){0,2}).*/\1/') +# UPDATED_VERSIONS_DICT=$(echo "$VERSIONS_DICT" | jq --arg k "$COMPONENT_PATH" --arg v "$RUNTIME_VERSION" '. + {($k): $v}') +# VERSIONS_DICT=$UPDATED_VERSIONS_DICT +# else +# RUNTIME_VERSION=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/.*@([0-9]+(\.[0-9]+){0,2}).*/\1/') +# UPDATED_VERSIONS_DICT=$(echo "$VERSIONS_DICT" | jq --arg k "default" --arg v "$RUNTIME_VERSION" '. + {($k): $v}') +# VERSIONS_DICT=$UPDATED_VERSIONS_DICT +# fi +# fi +# RUNTIME_LANGUAGE=$(echo "${RUNTIME_LANGUAGE,,}" | sed -E 's/@.*//') +# } + +# gatherRunTime "test" + +RUNTIME_LANGUAGE='{"frontend": "nodejs", "backend": "python@3.10"}' +echo "$RUNTIME_LANGUAGE" +RUNTIME_LANGUAGE=$(echo "$RUNTIME_LANGUAGE" \ + | jq -c '.frontend = "nodejs@3"') +echo "$RUNTIME_LANGUAGE" diff --git a/container-creation/intern-phxdc-pve1/etc-ssh/sshd_config b/container-creation/intern-phxdc-pve1/etc-ssh/sshd_config new file mode 100644 index 00000000..64f1e909 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/etc-ssh/sshd_config @@ -0,0 +1,141 @@ +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +Include /etc/ssh/sshd_config.d/*.conf + +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +PermitRootLogin yes +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +PubkeyAuthentication yes + +# Expect .ssh/authorized_keys2 to be disregarded by default in future. +#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +PasswordAuthentication yes +#PermitEmptyPasswords no + +# Change to yes to enable challenge-response passwords (beware issues with +# some PAM modules and threads) +KbdInteractiveAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes +#GSSAPIStrictAcceptorCheck yes +#GSSAPIKeyExchange no + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the KbdInteractiveAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via KbdInteractiveAuthentication may bypass +# the setting of "PermitRootLogin prohibit-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and KbdInteractiveAuthentication to 'no'. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +X11Forwarding yes +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +PrintMotd no +#PrintLastLog yes +#TCPKeepAlive yes +PermitUserEnvironment yes +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS no +#PidFile /run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server + +# Example of overriding settings on a per-user basis +Match User create-container + AcceptEnv AcceptEnv SSH_KEY_FP PUBLIC_KEY PROXMOX_USERNAME PROXMOX_PASSWORD CONTAINER_NAME CONTAINER_PASSWORD HTTP_PORT DEPLOY_ON_START PROJECT_REPOSITORY PROJECT_BRANCH PROJECT_ROOT REQUIRE_ENV_VARS CONTAINER_ENV_VARS INSTALL_COMMAND BUILD_COMMAND START_COMMAND RUNTIME_LANGUAGE SERVICES REQUIRE_SERVICES CUSTOM_SERVICES LINUX_DISTRIBUTION MULTI_COMPONENT ROOT_START_COMMAND GH_ACTION GITHUB_PAT SELF_HOSTED_RUNNER VERSIONS_DICT + ForceCommand /home/create-container/bin/extract-fingerprint.sh + PermitTTY yes + +Match User update-container + AcceptEnv CONTAINER_NAME PROXMOX_USERNAME PROXMOX_PASSWORD PROJECT_BRANCH PROJECT_ROOT PROJECT_REPOSITORY INSTALL_COMMAND BUILD_COMMAND START_COMMAND RUNTIME_LANGUAGE MULTI_COMPONENT ROOT_START_COMMAND DEPLOY_ON_START SERVICES CUSTOM_SERVICES REQUIRE_SERVICES LINUX_DISTRIBUTION GH_ACTION HTTP_PORT CONTAINER_ENV_VARS + ForceCommand sudo -E /var/lib/vz/snippets/update-container.sh + PermitTTY yes + +Match User container-exists + AcceptEnv PROXMOX_PASSWORD PROXMOX_USERNAME CONTAINER_NAME PROJECT_REPOSITORY DEPLOY_ON_START + ForceCommand sudo -E /var/lib/vz/snippets/container-exists.sh + PermitTTY yes + +Match User delete-container + AcceptEnv PROXMOX_PASSWORD PROXMOX_USERNAME CONTAINER_NAME PROJECT_REPOSITORY GITHUB_PAT + ForceCommand sudo -E /var/lib/vz/snippets/delete-container.sh + PermitTTY yes + +Match User setup-runner + AcceptEnv PROXMOX_USERNAME PROXMOX_PASSWORD CONTAINER_NAME CONTAINER_PASSWORD LINUX_DISTRIBUTION PROJECT_REPOSITORY GITHUB_PAT PROJECT_REPOSITORY + PermitTTY yes + ForceCommand sudo -E /var/lib/vz/snippets/setup-runner.sh + diff --git a/container-creation/intern-phxdc-pve1/home-create-container-bin/extract-fingerprint.sh b/container-creation/intern-phxdc-pve1/home-create-container-bin/extract-fingerprint.sh new file mode 100755 index 00000000..32e0e70b --- /dev/null +++ b/container-creation/intern-phxdc-pve1/home-create-container-bin/extract-fingerprint.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# A script to collect the client's SSH fingerprint and pass that to the content creation container +# Last Modified July 11, 2025 by Maxwell Klema +# --------------------- + +CURRENT_TIME=$(date +"%B %d %T") + +USER=$(whoami) +SSH_CLIENT_IP=$(echo $SSH_CLIENT | awk '{print $1}') +RECENT_LOG=$(journalctl --no-pager -r _COMM=sshd | grep "Accepted publickey for $USER from $SSH_CLIENT_IP" | head -n 1) +LOGGED_TIME=$(echo $RECENT_LOG | awk '{print $3}') + +#check most recent logged time and current time are only max 2 seconds off since multiple users may log in from same IP over time + +epoch1=$(date -d "today $CURRENT_TIME" +%s) +epoch2=$(date -d "today $LOGGED_TIME" +%s) +diff=$((epoch1 - epoch2)) + +KEY_FINGERPRINT="" + +if [ "$diff" -ge 0 ] && [ "$diff" -le 2 ]; then + KEY_FINGERPRINT=$(echo $RECENT_LOG | grep -o 'SHA256[^ ]*') +fi +export SSH_KEY_FP="$KEY_FINGERPRINT" + +# Export environment variables + +if [ "$USER" == "create-container" ]; then + VARS="AcceptEnv SSH_KEY_FP PUBLIC_KEY PROXMOX_USERNAME PROXMOX_PASSWORD CONTAINER_NAME CONTAINER_PASSWORD HTTP_PORT DEPLOY_ON_START PROJECT_REPOSITORY PROJECT_BRANCH PROJECT_ROOT REQUIRE_ENV_VARS CONTAINER_ENV_VARS INSTALL_COMMAND BUILD_COMMAND START_COMMAND RUNTIME_LANGUAGE SERVICES REQUIRE_SERVICES CUSTOM_SERVICES LINUX_DISTRIBUTION MULTI_COMPONENT ROOT_START_COMMAND GH_ACTION GITHUB_PAT SELF_HOSTED_RUNNER" + + for var in $VARS; do + export "$var"="${!var}" + done + + SEND_ENV=$(echo "$VARS" | tr '\n' ' '); + SEND_ENV="SSH_KEY_FP $SEND_ENV" + + ssh -o SendEnv="$SEND_ENV" create-container@10.15.234.122 +fi diff --git a/nginx-reverse-proxy/checkHostname.js b/nginx-reverse-proxy/checkHostname.js new file mode 100644 index 00000000..f3b322b6 --- /dev/null +++ b/nginx-reverse-proxy/checkHostname.js @@ -0,0 +1,22 @@ +var fs = require('fs'); +var filePath = "/etc/nginx/port_map.json"; +var cachedMapping = null; + +var content = fs.readFileSync(filePath); +cachedMapping = JSON.parse(content); + + +function checkHostnameExists(hostname) { + if (cachedMapping === null) { + console.error("Failed to load port mapping file."); + return false; + } + + if (!cachedMapping.hasOwnProperty(hostname)) { + return false; + } else { + return true; + } +} + +module.exports = { checkHostnameExists }; \ No newline at end of file diff --git a/nginx-reverse-proxy/checkHostnameRunner.js b/nginx-reverse-proxy/checkHostnameRunner.js new file mode 100644 index 00000000..eaf0de29 --- /dev/null +++ b/nginx-reverse-proxy/checkHostnameRunner.js @@ -0,0 +1,9 @@ +manageHostnames = require('./checkHostname.js'); + +const [,, func, ...args] = process.argv; +if (func == "checkHostnameExists"){ + console.log(manageHostnames.checkHostnameExists(...args)); +} else { + console.error("Invalid function name. Use 'checkHostnameExists' or 'addHostname'."); + process.exit(1); +} \ No newline at end of file From e29aa955ff83c86a4d748e923265f0cffc852fce Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Tue, 14 Oct 2025 16:25:48 -0400 Subject: [PATCH 2/7] add envvar documentation --- container-creation/README.md | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/container-creation/README.md b/container-creation/README.md index b0ea8a57..e594e088 100644 --- a/container-creation/README.md +++ b/container-creation/README.md @@ -80,4 +80,97 @@ If you'd like, I can also: - Add a simple diagram PNG export (requires mermaid-cli) and include it in the repo. - Turn the interactive prompts into optional CLI flags for automation. +Environment variables & prompts +-------------------------------- +This section lists the important environment variables that the container-creation scripts read, and which scripts will prompt interactively if those variables are not provided. It also notes special formats (JSON, base64) and scripts that treat some variables as encoded values. + +Notes about automation: +- Many scripts support a `GH_ACTION` environment variable (checked as `${GH_ACTION^^}`) which changes behavior for non-interactive CI runs. When `GH_ACTION=Y` some prompts will instead cause the script to fail fast with an error. +- Some inputs are expected as JSON (see entries below) and some are passed base64-encoded when the wrapper invokes remote scripts (see `create-container-wrapper.sh` and `create-container-new.sh`). + +Top-level scripts + +- create-lxc-container.sh (root-bin) + - Reads / prompts for: PROXMOX_USERNAME, PROXMOX_PASSWORD (prompts if unset), CONTAINER_NAME (prompts if unset), LINUX_DISTRIBUTION (prompts if unset; defaults to 'debian' if invalid), PUBLIC_KEY (optional; prompts if not auto-detected), HTTP_PORT (prompts if unset), USE_OTHER_PROTOCOLS (prompts), DEPLOY_ON_START (prompts if unset) + - Special/JSON vars accepted: CONTAINER_ENV_VARS (JSON), SERVICES (JSON), CUSTOM_SERVICES (JSON) + - Behavior: will prompt for missing values during interactive runs; in GH_ACTION mode missing values generally cause an immediate exit with an error code. + +- create-container-wrapper.sh (non-interactive wrapper) + - Reads required env vars and fails if missing: PROXMOX_USERNAME, PROXMOX_PASSWORD, CONTAINER_NAME, LINUX_DISTRIBUTION, HTTP_PORT + - Optional / additional envs: DEPLOY_ON_START (defaults to 'n'), AI_CONTAINER, PUBLIC_KEY, USE_OTHER_PROTOCOLS, OTHER_PROTOCOLS_LIST, OTHER_PORT, OTHER envs used for deployment (PROJECT_REPOSITORY, PROJECT_BRANCH, PROJECT_ROOT, INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, ENV_FOLDER, SERVICES_FILE, MULTI_COMPONENT, ROOT_START_COMMAND, SELF_HOSTED_RUNNER, VERSIONS_DICT) + - Notes: wrapper validates values (hostname format, http port range, distro choices) and will exit with an error message if validation fails — it is intended for fully-automated runs (CI or scripted environment where all required envs are provided). + +- create-container-new.sh / create-container.sh (hypervisor-side) + - Receives positional args from the wrapper; many deployment values are passed as base64-encoded strings (INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, VERSIONS_DICT) and then decoded inside the script. + - Uses/reads: CONTAINER_NAME, GH_ACTION, HTTP_PORT, PROXMOX_USERNAME, PUB_FILE, PROTOCOL_FILE, DEPLOY_ON_START, PROJECT_REPOSITORY, PROJECT_BRANCH, PROJECT_ROOT, INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, ENV_BASE_FOLDER, SERVICES_BASE_FILE, LINUX_DISTRO, MULTI_COMPONENTS, ROOT_START_COMMAND, SELF_HOSTED_RUNNER, VERSIONS_DICT + - Prompts: Generally does not prompt when invoked remotely by the wrapper — prompting occurs earlier in the wrapper or in the deploy-application flow. + +Deployment helper scripts (sourced by deploy-application.sh / create-lxc-container.sh) + +- deployment-scripts/deploy-application.sh + - Reads / prompts for: PROJECT_REPOSITORY (prompts if unset), PROJECT_BRANCH (prompts if unset; defaults to 'main'), PROJECT_ROOT (prompts if unset), MULTI_COMPONENT (prompts if unset), and then gathers setup commands and environment/runtime/services by sourcing helper scripts below. + - Validates repository and branch existence via curl and uses `js/runner.js authenticateRepo` to validate project root paths. + +- deployment-scripts/gatherEnvVars.sh + - Prompts interactively for environment variable key/value pairs when `REQUIRE_ENV_VARS` is set to 'Y' (prompts if `CONTAINER_ENV_VARS` JSON is not provided). + - Environment variables handled: REQUIRE_ENV_VARS, CONTAINER_ENV_VARS (JSON), ENV_FOLDER (output folder path set by the script) + - Behavior: creates temporary env files under `/root/bin/env/…` and will recurse to gather multiple variables/components. + +- deployment-scripts/gatherRuntimeLangs.sh + - Prompts for RUNTIME_LANGUAGE per component if not set via the `RUNTIME_LANGUAGE` env var (which can be JSON mapping component -> runtime). + - Environment variables: RUNTIME_LANGUAGE (JSON or single string), RT_ENV_VAR (internal), UNIQUE_COMPONENTS (sourced from deployment context) + +- deployment-scripts/gatherServices.sh + - Prompts for required services when `REQUIRE_SERVICES` is set (or reads `SERVICES` / `CUSTOM_SERVICES` env vars when provided). + - Environment variables: REQUIRE_SERVICES, SERVICES (JSON list), CUSTOM_SERVICES (JSON), SERVICES_FILE (output temporary file path) + - Behavior: validates requested services against `/root/bin/services/service_map_$LINUX_DISTRIBUTION.json` and writes install commands to a temporary services file. + +Runtime installers & deploy-on-start + +- helper-scripts/deployOnStart.sh + - Used inside hypervisor-side create script when DEPLOY_ON_START is 'Y'. It sources the deployment-scripts above and then performs runtime installation and service setup. + - Environment vars used: CONTAINER_ID, REPO/branch/root variables, VERSIONS_DICT, RUNTIME_LANGUAGE, INSTALL_COMMAND, START_COMMAND, MULTI_COMPONENTS, ENV_BASE_FOLDER, SERVICES_BASE_FILE + +- helper-scripts/node_runtime_install.sh + - Installs a Node.js major version inside the target container using the system package manager. Called with a major version argument. + - Environment variables used: CONTAINER_ID, PACKAGE_MANAGER (set by distro), MAJOR_VERSION (argument) + +- helper-scripts/python_runtime_install.sh + - Installs a specific Python version inside the target container. Called with distro and version arguments. + - Environment variables used: CONTAINER_ID, LINUX_DISTRO (argument), version (argument) + +Other utilities invoked during provisioning + +- helper-scripts/configureLDAP.sh + - Copies the generated `.env` into the container and downloads/runs `pown.sh` to configure SSSD/LDAP inside the container. + - Environment variables used: CONTAINER_ID, CONTAINER_IP, LINUX_DISTRO, and path `/var/lib/vz/snippets/.env` for the environment file. + - Prompts: none (runs non-interactively when called from create scripts). + +- Wazuh/register-agent.sh + - Registers the container with the Wazuh manager and installs the wazuh-agent inside the container. + - Environment variables used: CONTAINER_NAME, CONTAINER_IP, CONTAINER_ID + - Prompts: none (non-interactive when invoked from create scripts); it obtains the agent key via a `js/runner.js` call. + +Notes on JSON / encoded variables + +- `CONTAINER_ENV_VARS` — if present, expected as JSON mapping component -> key/value pairs. When provided, many scripts will avoid prompting and instead write env files from the JSON. +- `RUNTIME_LANGUAGE` — can be a single value (e.g., 'nodejs') or a JSON object mapping components to runtimes. When JSON is provided, `gatherRuntimeLangs.sh` will not prompt interactively. +- `VERSIONS_DICT`, `INSTALL_COMMAND`, `BUILD_COMMAND`, `START_COMMAND` — often passed between the wrapper and hypervisor scripts as base64-encoded strings and decoded on the other side. + +Quick reference table (script -> key env vars that may be read or prompted) + + - create-lxc-container.sh: PROXMOX_USERNAME*, PROXMOX_PASSWORD*, CONTAINER_NAME*, LINUX_DISTRIBUTION*, PUBLIC_KEY, HTTP_PORT*, USE_OTHER_PROTOCOLS, DEPLOY_ON_START + - create-container-wrapper.sh: PROXMOX_USERNAME*, PROXMOX_PASSWORD*, CONTAINER_NAME*, LINUX_DISTRIBUTION*, HTTP_PORT*, DEPLOY_ON_START + - create-container-new.sh / create-container.sh: (positional args from wrapper; many deployment vars passed as base64) + - deployment-scripts/deploy-application.sh: PROJECT_REPOSITORY*, PROJECT_BRANCH, PROJECT_ROOT*, MULTI_COMPONENT + - deployment-scripts/gatherEnvVars.sh: REQUIRE_ENV_VARS, CONTAINER_ENV_VARS (JSON) + - deployment-scripts/gatherRuntimeLangs.sh: RUNTIME_LANGUAGE (string or JSON) + - deployment-scripts/gatherServices.sh: REQUIRE_SERVICES, SERVICES (JSON), CUSTOM_SERVICES (JSON) + - helper-scripts/deployOnStart.sh: VERSIONS_DICT, RUNTIME_LANGUAGE, INSTALL_COMMAND, START_COMMAND, SERVICES_FILE + - helper-scripts/node_runtime_install.sh: CONTAINER_ID, MAJOR_VERSION (arg) + - helper-scripts/python_runtime_install.sh: CONTAINER_ID, LINUX_DISTRO (arg), version (arg) + - helper-scripts/configureLDAP.sh: CONTAINER_ID, CONTAINER_IP, LINUX_DISTRO + - Wazuh/register-agent.sh: CONTAINER_NAME, CONTAINER_IP, CONTAINER_ID + +Legend: variables marked with * are commonly prompted for when unset in interactive runs. \ No newline at end of file From 5d553cc5c875b622b40e0268bdd6af61c29c6e89 Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Tue, 14 Oct 2025 16:36:17 -0400 Subject: [PATCH 3/7] add more missing files --- .../var-lib-vz-snippets/container-exists.sh | 46 +++ .../var-lib-vz-snippets/delete-container.sh | 29 ++ .../var-lib-vz-snippets/setup-runner.sh | 141 ++++++++ .../var-lib-vz-snippets/update-container.sh | 324 ++++++++++++++++++ 4 files changed, 540 insertions(+) create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/container-exists.sh create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/delete-container.sh create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/setup-runner.sh create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/update-container.sh diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/container-exists.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/container-exists.sh new file mode 100755 index 00000000..ba0b2c9a --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/container-exists.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Script to check if a container exists, and if so, whether it needs to be updated or cloned. +# Last Modified by Maxwell Klema on July 13th, 2025 +# ----------------------------------------------------- + +outputError() { + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + echo -e "${BOLD}${MAGENTA}❌ Script Failed. Exiting... ${RESET}" + echo -e "$2" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + exit $1 +} + +RESET="\033[0m" +BOLD="\033[1m" +MAGENTA='\033[35m' + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}🔎 Check Container Exists ${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +TYPE_RUNNER="true" +source /var/lib/vz/snippets/helper-scripts/PVE_user_authentication.sh +source /var/lib/vz/snippets/helper-scripts/verify_container_ownership.sh + +STATUS=$? +if [ "$STATUS" != 0 ]; then + exit 1; +fi + +REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") + +# Check if repository folder is present. +if [ "$PVE1" == "true" ]; then + if [ ! -z "$CONTAINER_ID" ] && pct exec $CONTAINER_ID -- test -f /root/container-updates.log; then + exit 2; # Update Repository + else + exit 0; # Clone Repository + fi +else + if [ ! -z "$CONTAINER_ID" ] && ssh 10.15.0.5 "pct exec $CONTAINER_ID -- test -f /root/container-updates.log" ; then + exit 2; # Update Repository + else + exit 0; # Clone Repository + fi +fi \ No newline at end of file diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/delete-container.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/delete-container.sh new file mode 100755 index 00000000..d46ce23b --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/delete-container.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Script to delete a container permanently +# Last Modified by Maxwell Klema on July 13th, 2025 +# ----------------------------------------------------- + +RESET="\033[0m" +BOLD="\033[1m" +MAGENTA='\033[35m' + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}🗑️ Delete Container ${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +CMD=( +bash /var/lib/vz/snippets/helper-scripts/delete-runner.sh +"$PROJECT_REPOSITORY" +"$GITHUB_PAT" +"$PROXMOX_USERNAME" +"$PROXMOX_PASSWORD" +"$CONTAINER_NAME" +) + +# Safely quote each argument for the shell +QUOTED_CMD=$(printf ' %q' "${CMD[@]}") + +tmux new-session -d -s delete-runner "$QUOTED_CMD" + +echo "✅ Container with name \"$CONTAINER_NAME\" will be permanently deleted." +exit 0 # Container Deleted Successfully \ No newline at end of file diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/setup-runner.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/setup-runner.sh new file mode 100755 index 00000000..f56ddb26 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/setup-runner.sh @@ -0,0 +1,141 @@ +#!/bin/bash +# A script for cloning a Distro template, installing, and starting a runner on it. +# Last Modified by Maxwell Klema on August 5th, 2025 +# ------------------------------------------------ + +outputError() { + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + echo -e "${BOLD}${MAGENTA}❌ Script Failed. Exiting... ${RESET}" + echo -e "$2" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + exit $1 +} + +BOLD='\033[1m' +RESET='\033[0m' + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo "🧬 Cloning a Template and installing a Runner" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +# Validating Container Name ===== + +source /var/lib/vz/snippets/helper-scripts/PVE_user_authentication.sh #Authenticate User +source /var/lib/vz/snippets/helper-scripts/verify_container_ownership.sh #Ensure container does not exist. + +if [ ! -z "$CONTAINER_OWNERSHIP" ]; then + outputError 1 "You already own a container with name \"$CONTAINER_NAME\". What happened to the runner \"$CONTAINER_NAME\" in your repository? Either remove the setup-runner step from your workflow or delete the pre-existing container and try again." +fi + +# Cloning Container Template and Setting it up ===== + +REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") +REPO_BASE_NAME_WITH_OWNER=$(echo "$PROJECT_REPOSITORY" | cut -d'/' -f4) + +TEMPLATE_NAME="template-$REPO_BASE_NAME-$REPO_BASE_NAME_WITH_OWNER" +CTID_TEMPLATE=$( { pct list; ssh root@10.15.0.5 'pct list'; } | awk -v name="$TEMPLATE_NAME" '$3 == name {print $1}') + +case "${LINUX_DISTRIBUTION^^}" in + "") PACKAGE_MANAGER="apt-get" ;; + ROCKY) PACKAGE_MANAGER="dnf" ;; +esac + +# If no template ID was provided, assign a default based on distro + +if [ -z "$CTID_TEMPLATE" ]; then + case "${LINUX_DISTRIBUTION^^}" in + "") CTID_TEMPLATE="160" ;; + ROCKY) CTID_TEMPLATE="138" ;; + esac +fi + +if [ "${LINUX_DISTRIBUTION^^}" != "ROCKY" ]; then + LINUX_DISTRIBUTION="DEBIAN" +fi + +REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") +REPO_BASE_NAME_WITH_OWNER=$(echo "$PROJECT_REPOSITORY" | cut -d'/' -f4) + +NEXT_ID=$(pvesh get /cluster/nextid) #Get the next available LXC ID + +# Create the Container Clone +echo "⏳ Cloning Container..." +pct clone $CTID_TEMPLATE $NEXT_ID \ + --hostname $CONTAINER_NAME \ + --full true > /dev/null 2>&1 + +# Set Container Options +echo "⏳ Setting Container Properties..." +pct set $NEXT_ID \ + --tags "$PROXMOX_USERNAME" \ + --tags "$LINUX_DISTRIBUTION" \ + --onboot 1 \ + --cores 4 \ + --memory 4096 > /dev/null 2>&1 + +pct start $NEXT_ID > /dev/null 2>&1 +pveum aclmod /vms/$NEXT_ID --user "$PROXMOX_USERNAME@pve" --role VMUser2 > /dev/null 2>&1 + +sleep 5 +echo "⏳ DHCP Allocating IP Address..." +CONTAINER_IP=$(pct exec $NEXT_ID -- hostname -I | awk '{print $1}') + +# Setting Up Github Runner ===== + +# Get Temporary Token +echo "🪙 Getting Authentication Token..." +AUTH_TOKEN_RESPONSE=$(curl --location --request POST https://api.github.com/repos/$REPO_BASE_NAME_WITH_OWNER/$REPO_BASE_NAME/actions/runners/registration-token --header "Authorization: token $GITHUB_PAT" --write-out "HTTPSTATUS:%{http_code}" --silent) + +HTTP_STATUS=$(echo "$AUTH_TOKEN_RESPONSE" | grep -o "HTTPSTATUS:[0-9]*" | cut -d: -f2) +AUTH_TOKEN_BODY=$(echo "$AUTH_TOKEN_RESPONSE" | sed 's/HTTPSTATUS:[0-9]*$//') + +if [ "$HTTP_STATUS" != "201" ]; then + outputError 1 "Failed to get GitHub authentication token. HTTP Status: $HTTP_STATUS\nResponse: $AUTH_TOKEN_BODY" +fi + +TOKEN=$(echo "$AUTH_TOKEN_BODY" | jq -r '.token') + +pct enter $NEXT_ID < /dev/null 2>&1 +rm -rf /root/container-updates.log || true && \ +cd /actions-runner && export RUNNER_ALLOW_RUNASROOT=1 && \ +runProcess=\$(ps aux | grep "[r]un.sh" | awk '{print \$2}' | head -n 1) && \ +if [ ! -z "\$runProcess" ]; then kill -9 \$runProcess || true; fi && \ +rm -rf .runner .credentials && rm -rf _work/* /var/log/runner/* 2>/dev/null || true && \ +export RUNNER_ALLOW_RUNASROOT=1 && \ +./config.sh --url $PROJECT_REPOSITORY --token $TOKEN --labels $CONTAINER_NAME --name $CONTAINER_NAME --unattended +EOF + +# Generate RSA Keys ===== + +echo "🔑 Generating RSA Key Pair..." +pct exec $NEXT_ID -- bash -c "ssh-keygen -t rsa -N '' -f /root/.ssh/id_rsa -q" +PUB_KEY=$(pct exec $NEXT_ID -- bash -c "cat /root/.ssh/id_rsa.pub") + +# Place public key in all necessary authorized_keys files +echo "$PUB_KEY" >> /home/create-container/.ssh/authorized_keys +echo "$PUB_KEY" >> /home/update-container/.ssh/authorized_keys +echo "$PUB_KEY" >> /home/delete-container/.ssh/authorized_keys +echo "$PUB_KEY" >> /home/container-exists/.ssh/authorized_keys + +echo "🔑 Creating Service File..." +pct exec $NEXT_ID -- bash -c "cat < /etc/systemd/system/github-runner.service +[Unit] +Description=GitHub Actions Runner +After=network.target + +[Service] +Type=simple +WorkingDirectory=/actions-runner +Environment=\"RUNNER_ALLOW_RUNASROOT=1\" +ExecStart=/actions-runner/run.sh +Restart=always + +[Install] +WantedBy=multi-user.target +EOF" + +pct exec $NEXT_ID -- systemctl daemon-reload +pct exec $NEXT_ID -- systemctl enable github-runner +pct exec $NEXT_ID -- systemctl start github-runner + +exit 3 diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/update-container.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/update-container.sh new file mode 100755 index 00000000..0be5e739 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/update-container.sh @@ -0,0 +1,324 @@ +#!/bin/bash +# Script to automatically fetch new contents from a branch, push them to container, and restart intern +# Last Modified on August 17th, 2025 by Maxwell Klema +# ---------------------------------------- + +RESET="\033[0m" +BOLD="\033[1m" +MAGENTA='\033[35m' + +outputError() { + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + echo -e "${BOLD}${MAGENTA}❌ Script Failed. Exiting... ${RESET}" + echo -e "$2" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + exit $1 +} + + +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" +echo -e "${BOLD}${MAGENTA}🔄 Update Container Contents ${RESET}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" + +if [ -z "${RUNTIME_LANGUAGE^^}" ]; then + echo "Skipping container update because there is nothing to update." + exit 0 +fi + +source /var/lib/vz/snippets/helper-scripts/PVE_user_authentication.sh +source /var/lib/vz/snippets/helper-scripts/verify_container_ownership.sh + +# Get Project Details + +CONTAINER_NAME="${CONTAINER_NAME,,}" + +if [ -z "$PROJECT_REPOSITORY" ]; then + read -p "🚀 Paste the link to your project repository → " PROJECT_REPOSITORY +else + DEPLOY_ON_START="y" +fi + +CheckRepository() { + PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY#*github.com/} + PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY_SHORTENED%.git} + REPOSITORY_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED) +} + +CheckRepository + +if [ "$REPOSITORY_EXISTS" != "200" ]; then + outputError 1 "The repository link you provided, \"$PROJECT_REPOSITORY\" was not valid." +fi + +echo "✅ The repository link you provided, \"$PROJECT_REPOSITORY\", was valid." + +# Get Project Branch + +if [ -z "$PROJECT_BRANCH" ]; then + PROJECT_BRANCH="main" +fi + +REPOSITORY_BRANCH_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://api.github.com/repos/$PROJECT_REPOSITORY_SHORTENED/branches/$PROJECT_BRANCH) + +if [ "$REPOSITORY_BRANCH_EXISTS" != "200" ]; then + outputError 1 "The branch you provided, \"$PROJECT_BRANCH\", does not exist on repository at \"$PROJECT_REPOSITORY\"." +fi + + +# Get Project Root Directroy + +if [ "$PROJECT_ROOT" == "." ] || [ -z "$PROJECT_ROOT" ]; then + PROJECT_ROOT="/" +fi + +VALID_PROJECT_ROOT=$(ssh root@10.15.234.122 "node /root/bin/js/runner.js authenticateRepo \"$PROJECT_REPOSITORY\" \"$PROJECT_BRANCH\" \"$PROJECT_ROOT\"") + +if [ "$VALID_PROJECT_ROOT" == "false" ]; then + outputError 1 "The root directory you provided, \"$PROJECT_ROOT\", does not exist on branch, \"$PROJECT_BRANCH\", on repository at \"$PROJECT_REPOSITORY\"." +fi + +REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") +REPO_BASE_NAME_WITH_OWNER=$(echo "$PROJECT_REPOSITORY" | cut -d'/' -f4) + +if [ "$PROJECT_ROOT" == "" ] || [ "$PROJECT_ROOT" == "/" ]; then + PROJECT_ROOT="." +fi + +# Install Services ==== + +echo "🛎️ Installing Services..." + +if [ -z "$LINUX_DISTRIBUTION" ]; then + LINUX_DISTRIBUTION="debian" +fi + +if [ ! -z "$SERVICES" ] || [ ! -z "$CUSTOM_SERVICES" ]; then + REQUIRE_SERVICES="y" +fi + +SERVICE_COMMANDS=$(ssh -o SendEnv="LINUX_DISTRIBUTION SERVICES CUSTOM_SERVICES REQUIRE_SERVICES" \ + root@10.15.234.122 \ + "/root/bin/deployment-scripts/gatherServices.sh true") + +echo "$SERVICE_COMMANDS" | while read -r line; do + pct exec $CONTAINER_ID -- bash -c "$line | true" > /dev/null 2>&1 +done + +# Change HTTP port if necessary ==== + +if [ ! -z "$HTTP_PORT" ]; then + if [ "$HTTP_PORT" -lt 80 ] || [ "$HTTP_PORT" -gt 60000 ]; then + outputError 1 "Invalid HTTP port: $HTTP_PORT. Must be between 80 and 60000." + fi + ssh root@10.15.20.69 -- \ +"jq \ '.[\"$CONTAINER_NAME\"].ports.http = $HTTP_PORT' \ + /etc/nginx/port_map.json > /tmp/port_map.json.new \ + && mv -f /tmp/port_map.json.new /etc/nginx/port_map.json " +fi + +# Clone repository if needed ==== + +if (( "$CONTAINER_ID" % 2 == 0 )); then + ssh root@10.15.0.5 " + pct enter $CONTAINER_ID < /dev/null +fi +EOF + " +else + pct enter $CONTAINER_ID < /dev/null +fi +EOF +fi + +# Update Environment Variables + +if [ ! -z "$RUNTIME_LANGUAGE" ] && echo "$RUNTIME_LANGUAGE" | jq . >/dev/null 2>&1; then # If RUNTIME_LANGUAGE is set and is valid JSON + MULTI_COMPONENT="Y" +fi + +# Helper Function to write environment variables to a file inside container +writeEnvToFile() { + env_file_path="$1" + component_path="$2" + env_vars=$(cat "$env_file_path") + if (( $CONTAINER_ID % 2 == 0 )); then + ssh 10.15.0.5 "pct exec $CONTAINER_ID -- bash -c 'if [ ! -f \"$component_path/.env\" ]; then touch \"$component_path/.env\"; fi; echo \"$env_vars\" >> \"$component_path/.env\"'" + else + pct exec $CONTAINER_ID -- bash -c "if [ ! -f \"$component_path/.env\" ]; then touch \"$component_path/.env\"; fi; echo \"$env_vars\" >> \"$component_path/.env\"" + fi +} + +# Check If there are environment variables +if [ ! -z "$CONTAINER_ENV_VARS" ]; then + # generate random temp .env folder to store all env files for different components + RANDOM_NUM=$(shuf -i 100000-999999 -n 1) + ENV_FOLDER="env_$RANDOM_NUM" + ENV_FOLDER_PATH="/var/lib/vz/snippets/container-env-vars/$ENV_FOLDER" + mkdir -p "$ENV_FOLDER_PATH" + + if [ "${MULTI_COMPONENT^^}" == "Y" ]; then # Multi-Component + if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success + for key in $(echo "$CONTAINER_ENV_VARS" | jq -r 'keys[]'); do + COMPONENT_PATH="/root/$REPO_BASE_NAME/$PROJECT_ROOT/$key" + ENV_FILE_NAME=$(echo "$COMPONENT_PATH" | tr '/' '_') + ENV_FILE_NAME="$ENV_FILE_NAME.txt" + ENV_FILE_PATH="$ENV_FOLDER_PATH/$ENV_FILE_NAME" + touch "$ENV_FILE_PATH" + echo "$CONTAINER_ENV_VARS" | jq -r --arg key "$key" '.[$key] | to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" + writeEnvToFile "$ENV_FILE_PATH" "$COMPONENT_PATH" + done + else + outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS (GH_ACTION mode)" + exit 16 + fi + else # Single Component + ENV_FILE="env_$RANDOM_NUM.txt" + ENV_FILE_PATH="$ENV_FOLDER_PATH/$ENV_FILE" + touch "$ENV_FILE_PATH" + if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success + COMPONENT_PATH="/root/$REPO_BASE_NAME/$PROJECT_ROOT" + echo "$CONTAINER_ENV_VARS " | jq -r 'to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" #k=v pairs + writeEnvToFile "$ENV_FILE_PATH" "$COMPONENT_PATH" + else + outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." + writeLog "Invalid JSON in CONTAINER_ENV_VARS for single component (GH_ACTION mode)" + exit 16 + fi + fi +fi + +# Update Container with New Contents from repository ===== + +startComponentPVE1() { + + RUNTIME="$1" + BUILD_CMD="$2" + START_CMD="$3" + COMP_DIR="$4" + INSTALL_CMD="$5" + + if [ "${RUNTIME^^}" == "NODEJS" ]; then + pct set $CONTAINER_ID --memory 4096 --swap 0 --cores 4 + pct exec $CONTAINER_ID -- bash -c "cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/ && git fetch origin && git reset --hard origin/$PROJECT_BRANCH && git pull" > /dev/null 2>&1 + pct exec $CONTAINER_ID -- bash -c "cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/$COMP_DIR && $INSTALL_CMD && $BUILD_CMD" > /dev/null 2>&1 + pct set $CONTAINER_ID --memory 2048 --swap 0 --cores 2 + elif [ "${RUNTIME^^}" == "PYTHON" ]; then + pct set $CONTAINER_ID --memory 4096 --swap 0 --cores 4 + pct exec $CONTAINER_ID -- bash -c "cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/ && git fetch origin && git reset --hard origin/$PROJECT_BRANCH && git pull" > /dev/null 2>&1 + pct exec $CONTAINER_ID -- bash -c "cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/$COMP_DIR && source venv/bin/activate && $INSTALL_CMD && $BUILD_CMD" > /dev/null 2>&1 + pct set $CONTAINER_ID --memory 2048 --swap 0 --cores 2 + fi +} + +startComponentPVE2() { + + RUNTIME="$1" + BUILD_CMD="$2" + START_CMD="$3" + COMP_DIR="$4" + INSTALL_CMD="$5" + + if [ "${RUNTIME^^}" == "NODEJS" ]; then + ssh root@10.15.0.5 " + pct set $CONTAINER_ID --memory 4096 --swap 0 --cores 4 && + pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/ && git fetch origin && git reset --hard origin/$PROJECT_BRANCH && git pull' > /dev/null 2>&1 + pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/$COMP_DIR && $INSTALL_CMD' && '$BUILD_CMD' > /dev/null 2>&1 + pct set $CONTAINER_ID --memory 2048 --swap 0 --cores 2 + " + elif [ "${RUNTIME^^}" == "PYTHON" ]; then + ssh root@10.15.0.5 " + pct set $CONTAINER_ID --memory 4096 --swap 0 --cores 4 && + pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT && git fetch origin && git reset --hard origin/$PROJECT_BRANCH && git pull' > /dev/null 2>&1 + pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT/$COMP_DIR && source venv/bin/activate && $INSTALL_CMD' && '$BUILD_CMD' > /dev/null 2>&1 + pct set $CONTAINER_ID --memory 2048 --swap 0 --cores 2 + " + fi +} + +if [ "${MULTI_COMPONENT^^}" == "Y" ]; then + for COMPONENT in $(echo "$START_COMMAND" | jq -r 'keys[]'); do + START=$(echo "$START_COMMAND" | jq -r --arg k "$COMPONENT" '.[$k]') + RUNTIME=$(echo "$RUNTIME_LANGUAGE" | jq -r --arg k "$COMPONENT" '.[$k]') + BUILD=$(echo "$BUILD_COMMAND" | jq -r --arg k "$COMPONENT" '.[$k]') + INSTALL=$(echo "$INSTALL_COMMAND" | jq -r --arg k "$COMPONENT" '.[$k]') + if [ "$BUILD" == "null" ]; then + BUILD="" + fi + + if (( "$CONTAINER_ID" % 2 == 0 )); then + startComponentPVE2 "$RUNTIME" "$BUILD" "$START" "$COMPONENT" "$INSTALL" + else + startComponentPVE1 "$RUNTIME" "$BUILD" "$START" "$COMPONENT" "$INSTALL" + fi + done + if [ ! -z "$ROOT_START_COMMAND" ]; then + if (( $CONTAINER_ID % 2 == 0 )); then + ssh root@10.15.0.5 "pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT && $ROOT_START_COMMAND'" + else + pct exec $CONTAINER_ID -- bash -c "cd /root/$REPO_BASE_NAME/$PROJECT_ROOT && $ROOT_START_COMMAND" + fi + fi + # startComponent "$RUNTIME_LANGUAGE" "$BUILD_COMMAND" "$START_COMMAND" "." +else + if (( $CONTAINER_ID % 2 == 0 )); then + startComponentPVE2 "$RUNTIME_LANGUAGE" "$BUILD_COMMAND" "$START_COMMAND" "." "$INSTALL_COMMAND" + if [ ! -z "$ROOT_START_COMMAND" ]; then + ssh root@10.15.0.5 "pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT && $ROOT_START_COMMAND'" > /dev/null 2>&1 + fi + else + startComponentPVE1 "$RUNTIME_LANGUAGE" "$BUILD_COMMAND" "$START_COMMAND" "." "$INSTALL_COMMAND" + if [ ! -z "$ROOT_START_COMMAND" ]; then + ssh root@10.15.0.5 "pct exec $CONTAINER_ID -- bash -c 'cd /root/$REPO_BASE_NAME/$PROJECT_ROOT && $ROOT_START_COMMAND'" > /dev/null 2>&1 + fi + fi +fi + +# Update Log File + +if (( "$CONTAINER_ID" % 2 == 0 )); then + ssh root@10.15.0.5 "pct exec $CONTAINER_ID -- bash -c 'echo \"[$(date)]\" >> /root/container-updates.log'" +else + pct exec $CONTAINER_ID -- bash -c "echo \"[$(date)]\" >> /root/container-updates.log" +fi + +# Create new template if on default branch ===== + +UPDATE_CONTAINER="true" +BUILD_COMMAND_B64=$(echo -n "$BUILD_COMMAND" | base64) +RUNTIME_LANGUAGE_B64=$(echo -n "$RUNTIME_LANGUAGE" | base64) +START_COMMAND_B64=$(echo -n "$START_COMMAND" | base64) + +CMD=( +bash /var/lib/vz/snippets/start_services.sh +"$CONTAINER_ID" +"$CONTAINER_NAME" +"$REPO_BASE_NAME" +"$REPO_BASE_NAME_WITH_OWNER" +"$SSH_PORT" +"$CONTAINER_IP" +"$PROJECT_ROOT" +"$ROOT_START_COMMAND" +"$DEPLOY_ON_START" +"$MULTI_COMPONENT" +"$START_COMMAND_B64" +"$BUILD_COMMAND_B64" +"$RUNTIME_LANGUAGE_B64" +"$GH_ACTION" +"$PROJECT_BRANCH" +"$UPDATE_CONTAINER" +) + +# Safely quote each argument for the shell +QUOTED_CMD=$(printf ' %q' "${CMD[@]}") + +tmux new-session -d -s "$CONTAINER_NAME" "$QUOTED_CMD" +echo "✅ Container $CONTAINER_ID has been updated with new contents from branch \"$PROJECT_BRANCH\" on repository \"$PROJECT_REPOSITORY\"." +echo "Wait a few minutes for all background processes to complete before accessing the container." +exit 0 From 8b4187e93b2b7259ba7768d425152a2ed6a180b3 Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Mon, 20 Oct 2025 13:49:47 -0400 Subject: [PATCH 4/7] reorg and document --- Wazuh/register-agent.sh | 26 - container-creation/README.md | 189 +- container-creation/create-container-new.sh | 359 -- .../deployment-scripts/gatherEnvVars.sh | 111 - .../deployment-scripts/gatherRuntimeLangs.sh | 76 - .../deployment-scripts/gatherServices.sh | 158 - .../deployment-scripts/gatherSetupCommands.sh | 57 - container-creation/get-deployment-details.sh | 222 - .../get-lxc-container-details.sh | 332 -- .../helper-scripts/configureLDAP.sh | 51 - .../helper-scripts/deployOnStart.sh | 152 - .../helper-scripts/python_runtime_install.sh | 40 - .../var-lib-vz-snippets/.gitignore | 4 + .../var-lib-vz-snippets/Wazuh | 1 + .../create-container-new.sh | 2 +- .../var-lib-vz-snippets}/create-container.sh | 5 +- .../helper-scripts/PVE_user_authentication.sh | 10 +- .../helper-scripts}/configureLDAP.sh | 2 +- .../helper-scripts/create-template.sh | 0 .../helper-scripts/delete-runner.sh | 0 .../helper-scripts}/deployOnStart.sh | 0 .../helper-scripts/node_runtime_install.sh | 2 +- .../helper-scripts}/python_runtime_install.sh | 0 .../helper-scripts/repository_status.sh | 0 .../verify_container_ownership.sh | 3 +- .../var-lib-vz-snippets/opensource-server | 1 + .../register-container.sh | 2 +- .../register_proxy_hook.sh | 17 + .../var-lib-vz-snippets}/start_services.sh | 0 .../var-lib-vz-snippets/test.sh | 11 + container-creation/js/.gitignore | 12 - container-creation/js/README.md | 142 - container-creation/js/authenticateRepo.js | 23 - .../js/authenticateRepo.test.js | 241 - container-creation/js/authenticateUser.js | 28 - .../js/authenticateUser.test.js | 208 - container-creation/js/package-lock.json | 4061 ----------------- container-creation/js/package.json | 28 - container-creation/js/run-tests.sh | 68 - container-creation/js/runner.js | 13 - container-creation/js/runner.test.js | 44 - container-creation/node_runtime_install.sh | 10 - .../protocols/master_protocol_list.txt | 145 - .../services/service_map_debian.json | 69 - .../services/service_map_rocky.json | 99 - container-creation/ssh/detectPublicKey.sh | 25 - .../ssh/publicKeyAppendJumpHost.sh | 12 - 47 files changed, 64 insertions(+), 6997 deletions(-) delete mode 100644 Wazuh/register-agent.sh delete mode 100644 container-creation/create-container-new.sh delete mode 100644 container-creation/deployment-scripts/gatherEnvVars.sh delete mode 100755 container-creation/deployment-scripts/gatherRuntimeLangs.sh delete mode 100755 container-creation/deployment-scripts/gatherServices.sh delete mode 100644 container-creation/deployment-scripts/gatherSetupCommands.sh delete mode 100755 container-creation/get-deployment-details.sh delete mode 100644 container-creation/get-lxc-container-details.sh delete mode 100644 container-creation/helper-scripts/configureLDAP.sh delete mode 100644 container-creation/helper-scripts/deployOnStart.sh delete mode 100644 container-creation/helper-scripts/python_runtime_install.sh create mode 100644 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/.gitignore create mode 120000 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/Wazuh rename container-creation/{ssh => intern-phxdc-pve1/var-lib-vz-snippets}/create-container-new.sh (99%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/create-container.sh (98%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/PVE_user_authentication.sh (68%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts}/configureLDAP.sh (99%) rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/create-template.sh (100%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/delete-runner.sh (100%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts}/deployOnStart.sh (100%) rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/node_runtime_install.sh (99%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts}/python_runtime_install.sh (100%) rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/repository_status.sh (100%) mode change 100644 => 100755 rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/helper-scripts/verify_container_ownership.sh (98%) mode change 100644 => 100755 create mode 120000 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/opensource-server rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/register-container.sh (99%) mode change 100644 => 100755 create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register_proxy_hook.sh rename container-creation/{ => intern-phxdc-pve1/var-lib-vz-snippets}/start_services.sh (100%) mode change 100644 => 100755 create mode 100755 container-creation/intern-phxdc-pve1/var-lib-vz-snippets/test.sh delete mode 100644 container-creation/js/.gitignore delete mode 100644 container-creation/js/README.md delete mode 100644 container-creation/js/authenticateRepo.js delete mode 100644 container-creation/js/authenticateRepo.test.js delete mode 100644 container-creation/js/authenticateUser.js delete mode 100644 container-creation/js/authenticateUser.test.js delete mode 100644 container-creation/js/package-lock.json delete mode 100644 container-creation/js/package.json delete mode 100755 container-creation/js/run-tests.sh delete mode 100644 container-creation/js/runner.js delete mode 100644 container-creation/js/runner.test.js delete mode 100644 container-creation/node_runtime_install.sh delete mode 100644 container-creation/protocols/master_protocol_list.txt delete mode 100644 container-creation/services/service_map_debian.json delete mode 100644 container-creation/services/service_map_rocky.json delete mode 100755 container-creation/ssh/detectPublicKey.sh delete mode 100644 container-creation/ssh/publicKeyAppendJumpHost.sh diff --git a/Wazuh/register-agent.sh b/Wazuh/register-agent.sh deleted file mode 100644 index 724cd93a..00000000 --- a/Wazuh/register-agent.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Wazuh Registration Script to register an agent with the Wazuh manager -# Last Modified on August 6th, 2025 by Maxwell Klema -# ------------------------------------------------- - - -KEY=$(node /var/lib/vz/snippets/Wazuh/runner.js addAgent "$CONTAINER_NAME" "$CONTAINER_IP" | sed -n '2p') -MANAGER_IP="10.15.173.19" - -if [ "$KEY" == "fail" ]; then - echo "Failed to register agent with Wazuh manager." - exit 1 -fi - -# Install all necessary dependencies and register the agent to the manager - -pct enter "$CONTAINER_ID" -- <>Jump: ssh create-container@opensource.mieweb.org - Jump->>Extract: run extract-fingerprint.sh per sshd_config - Extract->>ProxySSH: pass certain ENV variables - ProxySSH->>ContainerServer: auto-ssh into 10.15.234.122 as create-container - ContainerServer->>Sudo: sshd_config triggers create-lxc-container.sh (runs as root via sudo) - Sudo->>Sudo: prompt for variables not supplied via ENV - Sudo->>JSRunner: invoke js/runner.js -> js/authenticateUser.js to validate credentials - Sudo->>NGINX: SSH to NGINX and run checkHostnameRunner.js -> checkHostname.js (validate hostname via port_map.json) - Sudo->>MasterProto: compare extra protocols against master_protocol_list.txt - Sudo->>DeployApp: if automatic deployment selected, call deploy-application.sh - DeployApp->>DeployApp: prompt for unset env vars - DeployApp->>RepoCheck: curl to check repository exists and is public - DeployApp->>JSRunner: invoke js/runner.js -> authenticateRepo.js - DeployApp->>Gather: invoke gatherSetupCommands.sh, gatherEnvVars.sh, gatherRuntimeLangs.sh, gatherServices.sh - Sudo->>SFTP: send user's public key, protocol file, env vars, and services to hypervisor via sftp - Sudo->>Hypervisor: invoke create-container.sh on intern-phxdc-pve1 over ssh - Hypervisor->>CreateCT: create-container.sh assigns CTID, clones CT template, sets tags, ACLs, waits for networking - CreateCT->>CreateCT: copy user's public key into container, generate random root password - CreateCT->>LDAP: run configureLDAP.sh - LDAP->>Pown: configureLDAP clones and runs pown.sh - CreateCT->>Wazuh: run register-agent.sh to install/configure wazuh manager IP - CreateCT->>DeployOnStart: call deployOnStart.sh if deployment enabled - DeployOnStart->>RepoCheck: clone project repo, copy env to .env - DeployOnStart->>RuntimeInstall: call node_runtime_install.sh or python_runtime_install.sh - RuntimeInstall->>RuntimeInstall: compile and install language runtime from source - DeployOnStart->>DeployOnStart: start services - CreateCT->>CreateCT: register container ports via register-container.sh - CreateCT->>CreateCT: launch tmux that invokes start_services.sh - -``` - -Notes: - -- The diagram closely follows the implementation: SSH restrictions are enforced via per-user `sshd_config` command= clauses and `extract-fingerprint.sh` establishes the environment and fingerprint forwarding. -- The system performs multiple, layered checks (user auth, hostname availability, protocol validation, repo existence) to avoid failed deployments. -- Several scripts prompt for missing variables so interactive runs are possible; these could be converted to fully-noninteractive flows by providing all required env vars. - -References: - -- See the `create-a-container/` folder for the front-end and server that initiate the flow. -- See the `container-creation/` folder for the server-side scripts invoked during provisioning. - -If you'd like, I can also: - -- Add a simple diagram PNG export (requires mermaid-cli) and include it in the repo. -- Turn the interactive prompts into optional CLI flags for automation. - -Environment variables & prompts --------------------------------- - -This section lists the important environment variables that the container-creation scripts read, and which scripts will prompt interactively if those variables are not provided. It also notes special formats (JSON, base64) and scripts that treat some variables as encoded values. - -Notes about automation: -- Many scripts support a `GH_ACTION` environment variable (checked as `${GH_ACTION^^}`) which changes behavior for non-interactive CI runs. When `GH_ACTION=Y` some prompts will instead cause the script to fail fast with an error. -- Some inputs are expected as JSON (see entries below) and some are passed base64-encoded when the wrapper invokes remote scripts (see `create-container-wrapper.sh` and `create-container-new.sh`). - -Top-level scripts - -- create-lxc-container.sh (root-bin) - - Reads / prompts for: PROXMOX_USERNAME, PROXMOX_PASSWORD (prompts if unset), CONTAINER_NAME (prompts if unset), LINUX_DISTRIBUTION (prompts if unset; defaults to 'debian' if invalid), PUBLIC_KEY (optional; prompts if not auto-detected), HTTP_PORT (prompts if unset), USE_OTHER_PROTOCOLS (prompts), DEPLOY_ON_START (prompts if unset) - - Special/JSON vars accepted: CONTAINER_ENV_VARS (JSON), SERVICES (JSON), CUSTOM_SERVICES (JSON) - - Behavior: will prompt for missing values during interactive runs; in GH_ACTION mode missing values generally cause an immediate exit with an error code. - -- create-container-wrapper.sh (non-interactive wrapper) - - Reads required env vars and fails if missing: PROXMOX_USERNAME, PROXMOX_PASSWORD, CONTAINER_NAME, LINUX_DISTRIBUTION, HTTP_PORT - - Optional / additional envs: DEPLOY_ON_START (defaults to 'n'), AI_CONTAINER, PUBLIC_KEY, USE_OTHER_PROTOCOLS, OTHER_PROTOCOLS_LIST, OTHER_PORT, OTHER envs used for deployment (PROJECT_REPOSITORY, PROJECT_BRANCH, PROJECT_ROOT, INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, ENV_FOLDER, SERVICES_FILE, MULTI_COMPONENT, ROOT_START_COMMAND, SELF_HOSTED_RUNNER, VERSIONS_DICT) - - Notes: wrapper validates values (hostname format, http port range, distro choices) and will exit with an error message if validation fails — it is intended for fully-automated runs (CI or scripted environment where all required envs are provided). - -- create-container-new.sh / create-container.sh (hypervisor-side) - - Receives positional args from the wrapper; many deployment values are passed as base64-encoded strings (INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, VERSIONS_DICT) and then decoded inside the script. - - Uses/reads: CONTAINER_NAME, GH_ACTION, HTTP_PORT, PROXMOX_USERNAME, PUB_FILE, PROTOCOL_FILE, DEPLOY_ON_START, PROJECT_REPOSITORY, PROJECT_BRANCH, PROJECT_ROOT, INSTALL_COMMAND, BUILD_COMMAND, START_COMMAND, RUNTIME_LANGUAGE, ENV_BASE_FOLDER, SERVICES_BASE_FILE, LINUX_DISTRO, MULTI_COMPONENTS, ROOT_START_COMMAND, SELF_HOSTED_RUNNER, VERSIONS_DICT - - Prompts: Generally does not prompt when invoked remotely by the wrapper — prompting occurs earlier in the wrapper or in the deploy-application flow. - -Deployment helper scripts (sourced by deploy-application.sh / create-lxc-container.sh) - -- deployment-scripts/deploy-application.sh - - Reads / prompts for: PROJECT_REPOSITORY (prompts if unset), PROJECT_BRANCH (prompts if unset; defaults to 'main'), PROJECT_ROOT (prompts if unset), MULTI_COMPONENT (prompts if unset), and then gathers setup commands and environment/runtime/services by sourcing helper scripts below. - - Validates repository and branch existence via curl and uses `js/runner.js authenticateRepo` to validate project root paths. - -- deployment-scripts/gatherEnvVars.sh - - Prompts interactively for environment variable key/value pairs when `REQUIRE_ENV_VARS` is set to 'Y' (prompts if `CONTAINER_ENV_VARS` JSON is not provided). - - Environment variables handled: REQUIRE_ENV_VARS, CONTAINER_ENV_VARS (JSON), ENV_FOLDER (output folder path set by the script) - - Behavior: creates temporary env files under `/root/bin/env/…` and will recurse to gather multiple variables/components. - -- deployment-scripts/gatherRuntimeLangs.sh - - Prompts for RUNTIME_LANGUAGE per component if not set via the `RUNTIME_LANGUAGE` env var (which can be JSON mapping component -> runtime). - - Environment variables: RUNTIME_LANGUAGE (JSON or single string), RT_ENV_VAR (internal), UNIQUE_COMPONENTS (sourced from deployment context) - -- deployment-scripts/gatherServices.sh - - Prompts for required services when `REQUIRE_SERVICES` is set (or reads `SERVICES` / `CUSTOM_SERVICES` env vars when provided). - - Environment variables: REQUIRE_SERVICES, SERVICES (JSON list), CUSTOM_SERVICES (JSON), SERVICES_FILE (output temporary file path) - - Behavior: validates requested services against `/root/bin/services/service_map_$LINUX_DISTRIBUTION.json` and writes install commands to a temporary services file. - -Runtime installers & deploy-on-start - -- helper-scripts/deployOnStart.sh - - Used inside hypervisor-side create script when DEPLOY_ON_START is 'Y'. It sources the deployment-scripts above and then performs runtime installation and service setup. - - Environment vars used: CONTAINER_ID, REPO/branch/root variables, VERSIONS_DICT, RUNTIME_LANGUAGE, INSTALL_COMMAND, START_COMMAND, MULTI_COMPONENTS, ENV_BASE_FOLDER, SERVICES_BASE_FILE - -- helper-scripts/node_runtime_install.sh - - Installs a Node.js major version inside the target container using the system package manager. Called with a major version argument. - - Environment variables used: CONTAINER_ID, PACKAGE_MANAGER (set by distro), MAJOR_VERSION (argument) - -- helper-scripts/python_runtime_install.sh - - Installs a specific Python version inside the target container. Called with distro and version arguments. - - Environment variables used: CONTAINER_ID, LINUX_DISTRO (argument), version (argument) - -Other utilities invoked during provisioning - -- helper-scripts/configureLDAP.sh - - Copies the generated `.env` into the container and downloads/runs `pown.sh` to configure SSSD/LDAP inside the container. - - Environment variables used: CONTAINER_ID, CONTAINER_IP, LINUX_DISTRO, and path `/var/lib/vz/snippets/.env` for the environment file. - - Prompts: none (runs non-interactively when called from create scripts). - -- Wazuh/register-agent.sh - - Registers the container with the Wazuh manager and installs the wazuh-agent inside the container. - - Environment variables used: CONTAINER_NAME, CONTAINER_IP, CONTAINER_ID - - Prompts: none (non-interactive when invoked from create scripts); it obtains the agent key via a `js/runner.js` call. - -Notes on JSON / encoded variables - -- `CONTAINER_ENV_VARS` — if present, expected as JSON mapping component -> key/value pairs. When provided, many scripts will avoid prompting and instead write env files from the JSON. -- `RUNTIME_LANGUAGE` — can be a single value (e.g., 'nodejs') or a JSON object mapping components to runtimes. When JSON is provided, `gatherRuntimeLangs.sh` will not prompt interactively. -- `VERSIONS_DICT`, `INSTALL_COMMAND`, `BUILD_COMMAND`, `START_COMMAND` — often passed between the wrapper and hypervisor scripts as base64-encoded strings and decoded on the other side. - -Quick reference table (script -> key env vars that may be read or prompted) - - - create-lxc-container.sh: PROXMOX_USERNAME*, PROXMOX_PASSWORD*, CONTAINER_NAME*, LINUX_DISTRIBUTION*, PUBLIC_KEY, HTTP_PORT*, USE_OTHER_PROTOCOLS, DEPLOY_ON_START - - create-container-wrapper.sh: PROXMOX_USERNAME*, PROXMOX_PASSWORD*, CONTAINER_NAME*, LINUX_DISTRIBUTION*, HTTP_PORT*, DEPLOY_ON_START - - create-container-new.sh / create-container.sh: (positional args from wrapper; many deployment vars passed as base64) - - deployment-scripts/deploy-application.sh: PROJECT_REPOSITORY*, PROJECT_BRANCH, PROJECT_ROOT*, MULTI_COMPONENT - - deployment-scripts/gatherEnvVars.sh: REQUIRE_ENV_VARS, CONTAINER_ENV_VARS (JSON) - - deployment-scripts/gatherRuntimeLangs.sh: RUNTIME_LANGUAGE (string or JSON) - - deployment-scripts/gatherServices.sh: REQUIRE_SERVICES, SERVICES (JSON), CUSTOM_SERVICES (JSON) - - helper-scripts/deployOnStart.sh: VERSIONS_DICT, RUNTIME_LANGUAGE, INSTALL_COMMAND, START_COMMAND, SERVICES_FILE - - helper-scripts/node_runtime_install.sh: CONTAINER_ID, MAJOR_VERSION (arg) - - helper-scripts/python_runtime_install.sh: CONTAINER_ID, LINUX_DISTRO (arg), version (arg) - - helper-scripts/configureLDAP.sh: CONTAINER_ID, CONTAINER_IP, LINUX_DISTRO - - Wazuh/register-agent.sh: CONTAINER_NAME, CONTAINER_IP, CONTAINER_ID - -Legend: variables marked with * are commonly prompted for when unset in interactive runs. \ No newline at end of file +## Scripts + +- [`extract-fingerprint.sh`](./intern-phxdc-pve1/home-create-container-bin/extract-fingerprint.sh) - Executed via the [`sshd_config`](./intern-phxdc-pve1/etc-ssh/sshd_config) `ForceCommand` for the "create-container" user on intern-phxdc-pve1. Derives user's SSH pubkey fingerprint from the sshd journal, then forwards the `SSH_KEY_FP`, `PUBLIC_KEY`, `PROXMOX_USERNAME`, `PROXMOX_PASSWORD`, `CONTAINER_NAME`, `HTTP_PORT`, `DEPLOY_ON_START`, `PROJECT_REPOSITORY`, `PROJECT_BRANCH`, `PROJECT_ROOT`, `REQUIRE_ENV_VARS`, `CONTAINER_ENV_VARS`, `INSTALL_COMMAND`, `BUILD_COMMAND`, `START_COMMAND`, `RUNTIME_LANGUAGE`, `SERVICES`, `REQUIRE_SERVICES`, `CUSTOM_SERVICES`, `LINUX_DISTRIBUTION`, `MULTI_COMPONENT`, `ROOT_START_COMMAND`, `GH_ACTION`, `GITHUB_PAT` and `SELF_HOSTED_RUNNER` environment variables to the ssh as "create-container" on the container-creation server. +- [`create-lxc-container.sh`](./container-creation/root-bin/create-lxc-container.sh) - Executed via the [`sshd_config`](./container-creation/etc-ssh/sshd_config) `ForceCommand` for the "create-container" user on container-creation. Queries for `PROXMOX_USERNAME`, `PROXMOX_PASSWORD` if unset in the environment, then verifies them with the [`runner.js`](./container-creation/root-bin/js/runner.js) subcommand `authenticateUser`. If authentication fails, the user is re-prompted unless the `GH_ACTION` environment variable is set, in which case the script fails fast. The script then prompts for `CONTAINER_NAME` if unset in the environment, then checks if that name is available with the [`checkHostnameRunner.js`](../nginx-reverse-proxy/checkHostnameRunner.js) script on the NGINX server. Again, the script re-prompts until a valid hostname is given unles `GH_ACTION` is set in the environment. The script then attempts to detect an SSH Public Key using the [`detectPublicKey.sh`](./container-creation/root-bin/ssh/detectPublicKey.sh) script and if it is not found, check if `PUBLIC_KEY` is unset, and otherwise prompt or fail depending on `GH_ACTION`. Once a public key is identified, it is handled with the [`publicKeyAppendJumpHost.sh`](./container-creation/root-bin/ssh/publicKeyAppendJumpHost.sh) script. Likewise, `HTTP_PORT` is prompted based on it's value and `GH_ACTION`. Then the user is prompted for additional protocols to be forwarded to the container cross-referenced with [`master_protocol_list.txt`](./container-creation/root-bin/protocols/master_protocol_list.txt). `DEPLOY_ON_START` is then prompted based on the value of `GH_ACTION`. If it is set, the [`deploy-application.sh`](./container-creation/root-bin/deploy-application.sh) script is invoked before continuing. Several files are then copied to intern-phxdc-pve1 before [`create-container.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh) is invoked as root on intern-phxdc-pve1. +- [`deploy-application.sh`](./container-creation/root-bin/deploy-application.sh) - This script prompts for the `PROJECT_REPOSITORY`, `PROJECT_BRANCH`, `PROJECT_ROOT`, `RUNTIME_LANGUAGE`, `MULTI_COMPONENT`, and `COMPONENT_PATH` variables if they are unset, failing early if `GH_ACTION` is defined. Each stage verfies the existence with the `authenticateRepo` subcommand of [`runner.js`](./container-creation/root-bin/js/runner.js). Several more variables are set using the [`gatherSetupCommands.sh`](./container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh), [`gatherEnvVars.sh`](./container-creation/root-bin/deployment-scripts/gatherEnvVars.sh), [`gatherRuntimeLangs.sh`](./container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh) and [`gatherServices.sh`](./container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh) scripts. +- [`gatherSetupCommands.sh`](./container-creation/root-bin/deployment-scripts/gatherSetupCommands.sh) - used to prompt for the `BUILD_COMMAND`, `INSTALL_COMMAND`, `START_COMMAND`, and `ROOT_START_COMMAND` (if `MUTLI_COMPONENT`) variables if they are unset. +- [`gatherEnvVars.sh`](./container-creation/root-bin/deployment-scripts/gatherEnvVars.sh) - used to prompt for the `REQUIRE_ENV_VARS` and `CONTAINER_ENV_VARS` then prompts for environment variables and their values to store in `/root/bin/env/env_{100000-999999}/{component_path}.txt` +- [`gatherRuntimeLangs.sh`](./container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh) - Prompts for the `RUNTIME_LANGUAGE` variable for each component. Support values include `nodejs` and `python` with a version number for both. +- [`gatherServices.sh`](./container-creation/root-bin/deployment-scripts/gatherRuntimeLangs.sh) - sets the `REQUIRE_SERVICES` variable, and, if true, appends services to the `/root/bin/services/services_{100000-999999}.txt` file which is a list of commands to run to start the services. +- [`create-container.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh) - Clones the container template for the `LINUX_DISTRO`, sets the `PROXMOX_USERNAME` tag, starts the container, adds `PROXMOX_USERNAME` as a user on the container, stores the `CONTAINER_ID`, adds the user's ssh public key, to root's authorized_keys, sets a random root password, stores `CONTAINER_IP` (allocated from DHCP but obtained from `hostname -I`), runs [`configureLDAP.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/configureLDAP.sh), runs [`register-agent.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/register-agent.sh), runs [`deployOnStart.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/deployOnStart.sh), runs [`register-container.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh), displays the container's details, then runs [`start_services.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh) in a TMUX. +- [`configureLDAP.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/configureLDAP.sh) - Runs https://raw.githubusercontent.com/anishapant21/pown.sh/main/pown.sh on thhe server, then makes a few additional changes the sssd. +- [`register-agent.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/register-agent.sh) - Installs wazuh-agent from it's repos then registers it with the manager. +- [`deployOnStart.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/deployOnStart.sh) - clones `PROJECT_REPOSITORY` to /root on the container, then copies each set of env vars from `gatherEnvVars.sh` to the appropriate component directories, then calls the appropriate [`node_runtime_install`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/node_runtime_install.sh) or [`python_runtime_install`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/python_runtime_install.sh) scripts for component runtimes, then runs `INSTALL_CMD` and executes the file created in `gatherServices.sh` +- [`register-container.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh) - interogates for `container_ip`, `hostname`, `os_release`, and `mac`, then checks if an SSH port forwards has already been allocated via IPTables on intern-phxdc-pve1 and allocates one if not. Similarly checks then allocates additional port-forwards also via IPTables on intern-phxdc-pve1. Then `port_map.json`is updated via an ssh connection to the NGINX container using `jq` to perform the update. +- [`start_services.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh) - Stops `CONTAINER_ID`, sources [`create-template.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/create-template.sh), starts `CONTAINER_ID`, starts `github-runner.service` in the container if `GH_ACTION`, runs `BUILD_COMMAND`, then `START_COMMAND` for each component and `ROOT_START_COMMAND` when `MULTI_COMPONENT`. \ No newline at end of file diff --git a/container-creation/create-container-new.sh b/container-creation/create-container-new.sh deleted file mode 100644 index 7890ede7..00000000 --- a/container-creation/create-container-new.sh +++ /dev/null @@ -1,359 +0,0 @@ -#!/bin/bash -# Script to create the pct container, run register container, and migrate container accordingly. -# Last Modified by October 3rd, 2025 by Carter Myers -# ----------------------------------------------------- - -BOLD='\033[1m' -BLUE='\033[34m' -MAGENTA='\033[35m' -GREEN='\033[32m' -RESET='\033[0m' - -cleanup() { - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - echo "⚠️ Script was abruptly exited. Running cleanup tasks." - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - pct_unlock $CTID_TEMPLATE - for file in \ - "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" \ - "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" \ - "/var/lib/vz/snippets/container-env-vars/$ENV_BASE_FOLDER" \ - "/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE" - do - [ -f "$file" ] && rm -rf "$file" - done - exit 1 -} - -echoContainerDetails() { - echo -e "📦 ${BLUE}Container ID :${RESET} $CONTAINER_ID" - echo -e "🌐 ${MAGENTA}Internal IP :${RESET} $CONTAINER_IP" - echo -e "🔗 ${GREEN}Domain Name :${RESET} https://$CONTAINER_NAME.opensource.mieweb.org" - echo -e "🛠️ ${BLUE}SSH Access :${RESET} ssh -p $SSH_PORT $PROXMOX_USERNAME@$CONTAINER_NAME.opensource.mieweb.org" - echo -e "🔑 ${BLUE}Container Password :${RESET} Your proxmox account password" - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - echo -e "${BOLD}${MAGENTA}NOTE: Additional background scripts are being ran in detached terminal sessions.${RESET}" - echo -e "${BOLD}${MAGENTA}Wait up to two minutes for all processes to complete.${RESET}" - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - echo -e "${BOLD}${BLUE}Still not working? Contact Max K. at maxklema@gmail.com${RESET}" - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" -} - -trap cleanup SIGINT SIGTERM SIGHUP - -CONTAINER_NAME="$1" -GH_ACTION="$2" -HTTP_PORT="$3" -PROXMOX_USERNAME="$4" -USERNAME_ONLY="${PROXMOX_USERNAME%@*}" -PUB_FILE="$5" -PROTOCOL_FILE="$6" - -# Deployment ENVS -DEPLOY_ON_START="$7" -PROJECT_REPOSITORY="$8" -PROJECT_BRANCH="$9" -PROJECT_ROOT="${10}" -INSTALL_COMMAND=$(echo "${11}" | base64 -d) -BUILD_COMMAND=$(echo "${12}" | base64 -d) -START_COMMAND=$(echo "${13}" | base64 -d) -RUNTIME_LANGUAGE=$(echo "${14}" | base64 -d) -ENV_BASE_FOLDER="${15}" -SERVICES_BASE_FILE="${16}" -LINUX_DISTRO="${17}" -MULTI_COMPONENTS="${18}" -ROOT_START_COMMAND="${19}" -SELF_HOSTED_RUNNER="${20}" -VERSIONS_DICT=$(echo "${21}" | base64 -d) -AI_CONTAINER="${22}" # new argument from HTML form - -echo "PROJECT ROOT: \"$PROJECT_ROOT\"" -echo "AI_CONTAINER: \"$AI_CONTAINER\"" - -# === Determine target PVE host based on AI_CONTAINER === -# PHOENIX -> 10.15.0.6 (existing AI host) -# FORTWAYNE -> 10.250.0.2 (new WireGuard-connected host) -# N -> local execution (no SSH proxy) -case "${AI_CONTAINER^^}" in - PHOENIX) - TARGET_PVE_HOST="10.15.0.6" - ;; - FORTWAYNE) - TARGET_PVE_HOST="10.250.0.2" - ;; - N|"" ) - TARGET_PVE_HOST="" - ;; - *) - echo "Invalid AI_CONTAINER value: $AI_CONTAINER" - exit 1 - ;; -esac - -# Helper: returns true if we're using a remote PVE host (PHOENIX or FORTWAYNE) -is_remote_pve() { - [[ -n "$TARGET_PVE_HOST" ]] -} - -# === Wrapper for pct exec (and optionally pct commands for remote PVE) === -run_pct_exec() { - local ctid="$1" - shift - if is_remote_pve; then - ssh root@"$TARGET_PVE_HOST" "pct exec $ctid -- $*" - else - pct exec "$ctid" -- "$@" - fi -} - -run_pct() { - # $@ = full pct command, e.g., clone, set, start, etc. - if is_remote_pve; then - ssh root@"$TARGET_PVE_HOST" "pct $*" - else - pct "$@" - fi -} - -run_pveum() { - # Wrapper for pveum commands in remote case - if is_remote_pve; then - ssh root@"$TARGET_PVE_HOST" "pveum $*" - else - pveum "$@" - fi -} - -run_pvesh() { - if is_remote_pve; then - ssh root@"$TARGET_PVE_HOST" "pvesh $*" - else - pvesh "$@" - fi -} - -run_pct_push() { - local ctid="$1" - local src="$2" - local dest="$3" - if is_remote_pve; then - ssh root@"$TARGET_PVE_HOST" "pct push $ctid $src $dest" - else - pct push "$ctid" "$src" "$dest" - fi -} - -# === Template Selection & Clone === -if [[ "${AI_CONTAINER^^}" == "PHOENIX" ]]; then - echo "⏳ Phoenix AI container requested. Using template CTID 163..." - CTID_TEMPLATE="163" - # Request cluster nextid from the target (remote if configured, else local via run_pvesh) - CONTAINER_ID=$(run_pvesh get /cluster/nextid) - - echo "DEBUG: Cloning on TARGET_PVE_HOST=${TARGET_PVE_HOST:-local} CTID_TEMPLATE=${CTID_TEMPLATE} -> CONTAINER_ID=${CONTAINER_ID}" - run_pct clone $CTID_TEMPLATE $CONTAINER_ID \ - --hostname $CONTAINER_NAME \ - --full true - - run_pct set $CONTAINER_ID \ - --tags "$PROXMOX_USERNAME" \ - --tags "$LINUX_DISTRO" \ - --tags "AI" \ - --onboot 1 - - run_pct start $CONTAINER_ID - run_pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser - -elif [[ "${AI_CONTAINER^^}" == "FORTWAYNE" ]]; then - echo "⏳ Fort Wayne AI container requested. Using template CTID 103 on 10.250.0.2..." - CTID_TEMPLATE="103" - # allocate nextid directly on Fort Wayne - CONTAINER_ID=$(ssh root@10.250.0.2 pvesh get /cluster/nextid) - - echo "DEBUG: Cloning on Fort Wayne (10.250.0.2) CTID_TEMPLATE=${CTID_TEMPLATE} -> CONTAINER_ID=${CONTAINER_ID}" - ssh root@10.250.0.2 pct clone $CTID_TEMPLATE $CONTAINER_ID \ - --hostname $CONTAINER_NAME \ - --full true - - ssh root@10.250.0.2 pct set $CONTAINER_ID \ - --tags "$PROXMOX_USERNAME" \ - --tags "$LINUX_DISTRO" \ - --tags "AI" \ - --onboot 1 - - ssh root@10.250.0.2 pct start $CONTAINER_ID - ssh root@10.250.0.2 pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser - -else - REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") - REPO_BASE_NAME_WITH_OWNER=$(echo "$PROJECT_REPOSITORY" | cut -d'/' -f4) - - TEMPLATE_NAME="template-$REPO_BASE_NAME-$REPO_BASE_NAME_WITH_OWNER" - # Search local and other known PVE (keeps original approach; will find local or remote templates depending on your environment) - CTID_TEMPLATE=$( { pct list; ssh root@10.15.0.5 'pct list'; } | awk -v name="$TEMPLATE_NAME" '$3 == name {print $1}') - - case "${LINUX_DISTRO^^}" in - DEBIAN) PACKAGE_MANAGER="apt-get" ;; - ROCKY) PACKAGE_MANAGER="dnf" ;; - esac - - if [ -z "$CTID_TEMPLATE" ]; then - case "${LINUX_DISTRO^^}" in - DEBIAN) CTID_TEMPLATE="160" ;; - ROCKY) CTID_TEMPLATE="138" ;; - esac - fi - - # For non-AI containers, allocate next ID locally and clone once here - CONTAINER_ID=$(pvesh get /cluster/nextid) - - echo "⏳ Cloning Container (non-AI)... CTID_TEMPLATE=${CTID_TEMPLATE} -> CONTAINER_ID=${CONTAINER_ID}" - run_pct clone $CTID_TEMPLATE $CONTAINER_ID \ - --hostname $CONTAINER_NAME \ - --full true - - echo "⏳ Setting Container Properties..." - run_pct set $CONTAINER_ID \ - --tags "$PROXMOX_USERNAME" \ - --tags "$LINUX_DISTRO" \ - --tags "LDAP" \ - --onboot 1 - - run_pct start $CONTAINER_ID - run_pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser -fi - -# === Post-Provisioning (pct exec wrapped) === -if [ -f "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" ]; then - echo "⏳ Appending Public Key..." - run_pct_exec $CONTAINER_ID touch ~/.ssh/authorized_keys > /dev/null 2>&1 - # Use a here-doc to reliably feed the pubkey to the remote pct exec - if is_remote_pve; then - # copy key file to remote PVE host temporarily then pct push it in case pct exec over ssh doesn't accept stdin redirection - scp "/var/lib/vz/snippets/container-public-keys/$PUB_FILE" root@"$TARGET_PVE_HOST":/tmp/"$PUB_FILE" > /dev/null 2>&1 || true - ssh root@"$TARGET_PVE_HOST" "pct push $CONTAINER_ID /tmp/$PUB_FILE /root/.ssh/authorized_keys >/dev/null 2>&1 || (pct exec $CONTAINER_ID -- bash -lc 'cat > ~/.ssh/authorized_keys' < /tmp/$PUB_FILE)" - ssh root@"$TARGET_PVE_HOST" "rm -f /tmp/$PUB_FILE" >/dev/null 2>&1 || true - else - run_pct_exec $CONTAINER_ID bash -c "cat > ~/.ssh/authorized_keys" < /var/lib/vz/snippets/container-public-keys/$PUB_FILE > /dev/null 2>&1 - rm -rf /var/lib/vz/snippets/container-public-keys/$PUB_FILE > /dev/null 2>&1 - fi -fi - -ROOT_PSWD=$(tr -dc 'A-Za-z0-9' /dev/null 2>&1 - -CONTAINER_IP="" -attempts=0 -max_attempts=10 - -while [[ -z "$CONTAINER_IP" && $attempts -lt $max_attempts ]]; do - CONTAINER_IP=$(run_pct_exec "$CONTAINER_ID" hostname -I | awk '{print $1}') - [[ -z "$CONTAINER_IP" ]] && sleep 2 && ((attempts++)) -done - -if [[ -z "$CONTAINER_IP" ]]; then - echo "❌ Timed out waiting for container to get an IP address." - exit 1 -fi - -echo "⏳ Updatng container packages..." -if [[ "${LINUX_DISTRO^^}" == "ROCKY" ]]; then - run_pct_exec $CONTAINER_ID bash -c "dnf upgrade -y" -else - run_pct_exec $CONTAINER_ID bash -c "apt-get update && apt-get upgrade -y" -fi - -echo "⏳ Configuring LDAP connection via SSSD..." -export AI_CONTAINER="$AI_CONTAINER" -source /var/lib/vz/snippets/helper-scripts/configureLDAP.sh - -echo "⏳ Setting up Wazuh-Agent..." -source /var/lib/vz/snippets/Wazuh/register-agent.sh - -if [ "${DEPLOY_ON_START^^}" == "Y" ]; then - source /var/lib/vz/snippets/helper-scripts/deployOnStart.sh - for file in \ - "/var/lib/vz/snippets/container-env-vars/$ENV_BASE_FOLDER" \ - "/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE" - do - [ -f "$file" ] && rm -rf "$file" > /dev/null 2>&1 - done -fi - -run_pct_exec $CONTAINER_ID bash -c "cd /root && touch container-updates.log" - -echo "⏳ Running Container Provision Script..." -if [ -f "/var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE" ]; then - /var/lib/vz/snippets/register-container.sh $CONTAINER_ID $HTTP_PORT /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE "$USERNAME_ONLY" - rm -rf /var/lib/vz/snippets/container-port-maps/$PROTOCOL_FILE > /dev/null 2>&1 -else - /var/lib/vz/snippets/register-container.sh $CONTAINER_ID $HTTP_PORT "" "$PROXMOX_USERNAME" -fi - -SSH_PORT=$(iptables -t nat -S PREROUTING | grep "to-destination $CONTAINER_IP:22" | awk -F'--dport ' '{print $2}' | awk '{print $1}' | head -n 1 || true) - -echo "Adding container MOTD information..." -# port_map.json remains on central nginx host (10.15.20.69) — leave as-is unless you want to change that behavior -scp 10.15.20.69:/etc/nginx/port_map.json /tmp/port_map.json -CONTAINER_INFO=$(jq -r --arg hn "$CONTAINER_NAME" '.[$hn]' /tmp/port_map.json) - -if [ "$CONTAINER_INFO" != "null" ]; then - HOSTNAME="$CONTAINER_NAME" - IP=$(echo "$CONTAINER_INFO" | jq -r '.ip') - OWNER=$(echo "$CONTAINER_INFO" | jq -r '.user') - OS_RELEASE=$(echo "$CONTAINER_INFO" | jq -r '.os_release') - PORTS=$(echo "$CONTAINER_INFO" | jq -r '.ports | to_entries[] | "\(.key): \(.value)"' | paste -sd ", " -) - PROTOCOLS=$(echo "$CONTAINER_INFO" | jq -r '.ports | keys | join(", ")') - - cat < /tmp/container_motd -Container Information: ----------------------- -Hostname : $HOSTNAME -IP Address : $IP -Ports : $PORTS -Protocols : $PROTOCOLS -Primary Owner : $OWNER -OS Release : $OS_RELEASE -EOF -else - echo "No container info found for $CONTAINER_NAME" > /tmp/container_motd -fi - -run_pct_push $CONTAINER_ID /tmp/container_motd /etc/motd - -echoContainerDetails - -BUILD_COMMAND_B64=$(echo -n "$BUILD_COMMAND" | base64) -RUNTIME_LANGUAGE_B64=$(echo -n "$RUNTIME_LANGUAGE" | base64) -START_COMMAND_B64=$(echo -n "$START_COMMAND" | base64) - -# Only run start_services when this is NOT an AI container (previously referenced undefined $AI) -if [[ "${AI_CONTAINER^^}" != "PHOENIX" && "${AI_CONTAINER^^}" != "FORTWAYNE" ]]; then - CMD=( - bash /var/lib/vz/snippets/start_services.sh - "$CONTAINER_ID" - "$CONTAINER_NAME" - "$REPO_BASE_NAME" - "$REPO_BASE_NAME_WITH_OWNER" - "$SSH_PORT" - "$CONTAINER_IP" - "$PROJECT_ROOT" - "$ROOT_START_COMMAND" - "$DEPLOY_ON_START" - "$MULTI_COMPONENTS" - "$START_COMMAND_B64" - "$BUILD_COMMAND_B64" - "$RUNTIME_LANGUAGE_B64" - "$GH_ACTION" - "$PROJECT_BRANCH" - ) -fi - -QUOTED_CMD=$(printf ' %q' "${CMD[@]}") - -# Create detached tmux session to run the (possibly long) service start process -if [[ -n "${CMD[*]}" ]]; then - tmux new-session -d -s "$CONTAINER_NAME" "$QUOTED_CMD" -fi - -exit 0 \ No newline at end of file diff --git a/container-creation/deployment-scripts/gatherEnvVars.sh b/container-creation/deployment-scripts/gatherEnvVars.sh deleted file mode 100644 index 9453c065..00000000 --- a/container-creation/deployment-scripts/gatherEnvVars.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -# Helper function to gather environment variables -gatherEnvVars(){ - TEMP_ENV_FILE_PATH="$1" - - read -p "🔑 Enter Environment Variable Key → " ENV_VAR_KEY - read -p "🔑 Enter Environment Variable Value → " ENV_VAR_VALUE - - while [ "$ENV_VAR_KEY" == "" ] || [ "$ENV_VAR_VALUE" == "" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Key and value cannot be empty. Please try again." - writeLog "Empty environment variable key or value entered (GH_ACTION mode)" - exit 15 - fi - echo "⚠️ Key or value cannot be empty. Try again." - writeLog "Empty environment variable key or value entered" - read -p "🔑 Enter Environment Variable Key → " ENV_VAR_KEY - read -p "🔑 Enter Environment Variable Value → " ENV_VAR_VALUE - done - - echo "$ENV_VAR_KEY=$ENV_VAR_VALUE" >> $TEMP_ENV_FILE_PATH - - read -p "🔑 Do you want to enter another Environment Variable? (y/n) → " ENTER_ANOTHER_ENV - - while [ "${ENTER_ANOTHER_ENV^^}" == "Y" ]; do - gatherEnvVars "$TEMP_ENV_FILE_PATH" - done -} - -if [ -z "$REQUIRE_ENV_VARS" ]; then - read -p "🔑 Does your application require environment variables? (y/n) → " REQUIRE_ENV_VARS -fi - -while [ "${REQUIRE_ENV_VARS^^}" != "Y" ] && [ "${REQUIRE_ENV_VARS^^}" != "N" ] && [ "${REQUIRE_ENV_VARS^^}" != "" ]; do - echo "⚠️ Invalid option. Please try again." - writeLog "Invalid environment variables requirement option entered: $REQUIRE_ENV_VARS" - read -p "🔑 Does your application require environment variables? (y/n) → " REQUIRE_ENV_VARS -done - -if [ "${GH_ACTION^^}" == "Y" ]; then - if [ ! -z "$CONTAINER_ENV_VARS" ]; then - REQUIRE_ENV_VARS="Y" - fi -fi - -if [ "${REQUIRE_ENV_VARS^^}" == "Y" ]; then - # generate random temp .env folder to store all env files for different components - RANDOM_NUM=$(shuf -i 100000-999999 -n 1) - ENV_FOLDER="env_$RANDOM_NUM" - ENV_FOLDER_PATH="/root/bin/env/$ENV_FOLDER" - mkdir -p "$ENV_FOLDER_PATH" - - if [ "${MULTI_COMPONENT^^}" == "Y" ]; then - if [ ! -z "$CONTAINER_ENV_VARS" ]; then # Environment Variables - if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success - for key in $(echo "$CONTAINER_ENV_VARS" | jq -r 'keys[]'); do - gatherComponentDir "Enter the path of your component to enter environment variables" "$key" - ENV_FILE_NAME=$(echo "$COMPONENT_PATH" | tr '/' '_') - ENV_FILE_NAME="$ENV_FILE_NAME.txt" - ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE_NAME" - touch "$ENV_FILE_PATH" - echo "$CONTAINER_ENV_VARS" | jq -r --arg key "$key" '.[$key] | to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" - addComponent "$key" - done - else - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in CONTAINER_ENV_VARS (GH_ACTION mode)" - exit 16 - fi - echo "⚠️ Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in CONTAINER_ENV_VARS" - exit 16 - fi - else # No Environment Variables - gatherComponentDir "Enter the path of your component to enter environment variables" - - while [ "$COMPONENT_PATH" != "" ]; do - addComponent "$COMPONENT_PATH" - ENV_FILE_NAME=$(echo "$COMPONENT_PATH" | tr '/' '_') - ENV_FILE="$ENV_FILE_NAME.txt" - ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE" - touch "$ENV_FILE_PATH" - gatherEnvVars "$ENV_FILE_PATH" - gatherComponentDir "Enter the path of your component to enter environment variables" - done - fi - else # Single Component - ENV_FILE="env_$RANDOM_NUM.txt" - ENV_FILE_PATH="/root/bin/env/$ENV_FOLDER/$ENV_FILE" - touch "$ENV_FILE_PATH" - - if [ ! -z "$CONTAINER_ENV_VARS" ]; then # Environment Variables - if echo "$CONTAINER_ENV_VARS" | jq -e > /dev/null 2>&1; then #if exit status of jq is 0 (valid JSON) // success - echo "$CONTAINER_ENV_VARS " | jq -r 'to_entries[] | "\(.key)=\(.value)"' > "$ENV_FILE_PATH" #k=v pairs - else - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in CONTAINER_ENV_VARS for single component (GH_ACTION mode)" - exit 16 - fi - echo "⚠️ Your \"CONTAINER_ENV_VARS\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in CONTAINER_ENV_VARS for single component" - exit 16 - fi - else # No Environment Variables - gatherEnvVars "$ENV_FILE_PATH" - fi - fi -fi \ No newline at end of file diff --git a/container-creation/deployment-scripts/gatherRuntimeLangs.sh b/container-creation/deployment-scripts/gatherRuntimeLangs.sh deleted file mode 100755 index 508eff5c..00000000 --- a/container-creation/deployment-scripts/gatherRuntimeLangs.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -gatherRunTime() { - COMPONENT_PATH="$1" - - if [ -z "${RUNTIME_LANGUAGE}" ] || [ "$RT_ENV_VAR" != "true" ]; then - read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE - fi - - while [ "${RUNTIME_LANGUAGE^^}" != "NODEJS" ] && [ "${RUNTIME_LANGUAGE^^}" != "PYTHON" ]; do - echo "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." - writeLog "Unsupported runtime environment entered: $RUNTIME_LANGUAGE for component: $COMPONENT_PATH" - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "⚠️ Sorry, that runtime environment is not yet supported. Only \"nodejs\" and \"python\" are currently supported." - writeLog "Unsupported runtime environment entered: $RUNTIME_LANGUAGE (GH_ACTION mode)" - exit 17 - fi - read -p "🖥️ Enter the underlying runtime environment for \"$COMPONENT_PATH\" (e.g., 'nodejs', 'python') → " RUNTIME_LANGUAGE - done -} - -# Helper function to remove an item from a list -removeFromList() { - ITEM_TO_REMOVE="$1" - NEW_LIST=() - for ITEM in "${UNIQUE_COMPONENTS_CLONE[@]}"; do - if [ "$ITEM" != "$ITEM_TO_REMOVE" ]; then - NEW_LIST+=("$ITEM") - fi - done - UNIQUE_COMPONENTS_CLONE=("${NEW_LIST[@]}") -} - -UNIQUE_COMPONENTS_CLONE=("${UNIQUE_COMPONENTS[@]}") -RUNTIME_LANGUAGE_DICT={} - - -if [ "${MULTI_COMPONENT^^}" == 'Y' ]; then - if [ ! -z "$RUNTIME_LANGUAGE" ]; then # Environment Variable Passed - if echo "$RUNTIME_LANGUAGE" | jq -e > /dev/null 2>&1; then # Valid JSON - for key in $(echo "$RUNTIME_LANGUAGE" | jq -r 'keys[]'); do - removeFromList "$key" - done - if [ ${#UNIQUE_COMPONENTS_CLONE[@]} -gt 0 ]; then #if there are still components in the list, then not all runtimes were provided, so exit on error - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "You did not provide runtime languages for these components: \"${UNIQUE_COMPONENTS_CLONE[@]}\"." - writeLog "Missing runtime languages for components: ${UNIQUE_COMPONENTS_CLONE[@]} (GH_ACTION mode)" - exit 18 - fi - echo "⚠️ You did not provide runtime languages for these components: \"${UNIQUE_COMPONENTS_CLONE[@]}\"." - writeLog "Missing runtime languages for components: ${UNIQUE_COMPONENTS_CLONE[@]}" - exit 18 - fi - else - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Your \"$RUNTIME_LANGUAGE\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in RUNTIME_LANGUAGE (GH_ACTION mode)" - exit 16 - fi - echo "⚠️ Your \"$RUNTIME_LANGUAGE\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in RUNTIME_LANGUAGE" - exit 16 - fi - else # No Environment Variable Passed - for CURRENT in "${UNIQUE_COMPONENTS[@]}"; do - gatherRunTime "$CURRENT" - RUNTIME_LANGUAGE_DICT=$(echo "$RUNTIME_LANGUAGE_DICT" | jq --arg k "$CURRENT" --arg v "$RUNTIME_LANGUAGE" '. + {($k): $v}') - done - RUNTIME_LANGUAGE=$RUNTIME_LANGUAGE_DICT - fi -else - if [ ! -z "$RUNTIME_LANGUAGE" ]; then - RT_ENV_VAR="true" - fi - gatherRunTime "$PROJECT_REPOSITORY" -fi \ No newline at end of file diff --git a/container-creation/deployment-scripts/gatherServices.sh b/container-creation/deployment-scripts/gatherServices.sh deleted file mode 100755 index 1cc06c39..00000000 --- a/container-creation/deployment-scripts/gatherServices.sh +++ /dev/null @@ -1,158 +0,0 @@ -SERVICE_MAP="/root/bin/services/service_map_$LINUX_DISTRIBUTION.json" -APPENDED_SERVICES=() - -# Helper function to check if a user has added the same service twice -serviceExists() { - SERVICE="$1" - for CURRENT in "${APPENDED_SERVICES[@]}"; do - if [ "${SERVICE,,}" == "${CURRENT,,}" ]; then - return 0 - fi - done - return 1 -} - -processService() { - local SERVICE="$1" - local MODE="$2" # "batch" or "single" - - SERVICE_IN_MAP=$(jq -r --arg key "${SERVICE,,}" '.[$key] // empty' "$SERVICE_MAP") - if serviceExists "$SERVICE"; then - if [ "$MODE" = "batch" ]; then - return 0 # skip to next in batch mode - else - echo "⚠️ You already added \"$SERVICE\" as a service. Please try again." - writeLog "Duplicate service attempted: $SERVICE" - return 0 - fi - elif [ "${SERVICE^^}" != "C" ] && [ "${SERVICE^^}" != "" ] && [ -n "$SERVICE_IN_MAP" ]; then - jq -r --arg key "${SERVICE,,}" '.[$key][]' "$SERVICE_MAP" >> "$TEMP_SERVICES_FILE_PATH" - echo "sudo systemctl daemon-reload" >> "$TEMP_SERVICES_FILE_PATH" - echo "✅ ${SERVICE^^} added to your container." - APPENDED_SERVICES+=("${SERVICE^^}") - elif [ "${SERVICE^^}" == "C" ]; then - appendCustomService - elif [ "${SERVICE^^}" != "" ]; then - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "⚠️ Service \"$SERVICE\" does not exist." - writeLog "Invalid service entered: $SERVICE (GH_ACTION mode)" - exit 20 - fi - echo "⚠️ Service \"$SERVICE\" does not exist." - writeLog "Invalid service entered: $SERVICE" - [ "$MODE" = "batch" ] && exit 20 - fi -} - -# Helper function to append a new service to a container -appendService() { - if [ ! -z "$SERVICES" ]; then - for SERVICE in $(echo "$SERVICES" | jq -r '.[]'); do - processService "$SERVICE" "batch" - done - else - read -p "➡️ Enter the name of a service to add to your container or type \"C\" to set up a custom service installation (Enter to exit) → " SERVICE - processService "$SERVICE" "single" - fi -} - -appendCustomService() { - # If there is an env variable for custom services, iterate through each command and append it to temporary services file - if [ ! -z "$CUSTOM_SERVICES" ]; then - echo "$CUSTOM_SERVICES" | jq -c -r '.[]' | while read -r CUSTOM_SERVICE; do - echo "$CUSTOM_SERVICE" | jq -c -r '.[]' | while read -r CUSTOM_SERVICE_COMMAND; do - if [ ! -z "$CUSTOM_SERVICE_COMMAND" ]; then - echo "$CUSTOM_SERVICE_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" - else - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "⚠️ Custom Service Installation Command cannot be empty in \"$CUSTOM_SERVICE\"." - writeLog "Empty custom service command in: $CUSTOM_SERVICE (GH_ACTION mode)" - exit 21 - fi - echo "⚠️ Command cannot be empty." - writeLog "Empty custom service command in: $CUSTOM_SERVICE" - exit 21; - fi - done - done - echo "✅ Custom Services appended." - else - echo "🛎️ Configuring Custom Service Installation. For each prompt, enter a command that is a part of the installation process for your service on Debian Bookworm. Do not forget to enable and start the service at the end. Once you have entered all of your commands, press enter to continue" - COMMAND_NUM=1 - read -p "➡️ Enter Command $COMMAND_NUM: " CUSTOM_COMMAND - - echo "$CUSTOM_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" - - while [ "${CUSTOM_COMMAND^^}" != "" ]; do - ((COMMAND_NUM++)) - read -p "➡️ Enter Command $COMMAND_NUM: " CUSTOM_COMMAND - echo "$CUSTOM_COMMAND" >> "$TEMP_SERVICES_FILE_PATH" - done - fi -} - -# Helper function to see if a user wants to set up a custom service -setUpService() { - read -p "🛎️ Do you wish to set up a custom service installation? (y/n) " SETUP_CUSTOM_SERVICE_INSTALLATION - while [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "Y" ] && [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "N" ] && [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" != "" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "⚠️ Invalid custom service installation option. Please try again." - writeLog "Invalid custom service installation option entered: $SETUP_CUSTOM_SERVICE_INSTALLATION (GH_ACTION mode)" - exit 22 - fi - echo "⚠️ Invalid option. Please try again." - writeLog "Invalid custom service installation option entered: $SETUP_CUSTOM_SERVICE_INSTALLATION" - read -p "🛎️ Do you wish to set up a custom service installation? (y/n) " SETUP_CUSTOM_SERVICE_INSTALLATION - done -} - -if [ -z "$REQUIRE_SERVICES" ]; then - read -p "🛎️ Does your application require special services (i.e. Docker, MongoDB, etc.) to run on the container? (y/n) → " REQUIRE_SERVICES -fi - -while [ "${REQUIRE_SERVICES^^}" != "Y" ] && [ "${REQUIRE_SERVICES^^}" != "N" ] && [ "${REQUIRE_SERVICES^^}" != "" ]; do - echo "⚠️ Invalid option. Please try again." - writeLog "Invalid service requirement option entered: $REQUIRE_SERVICES" - read -p "🛎️ Does your application require special services (i.e. Docker, MongoDB, etc.) to run on the container? (y/n) → " REQUIRE_SERVICES -done - -if [ "${GH_ACTION^^}" == "Y" ]; then - if [ ! -z "$SERVICES" ] || [ ! -z "$CUSTOM_SERVICES" ]; then - REQUIRE_SERVICES="Y" - fi -fi - -if [ "${REQUIRE_SERVICES^^}" == "Y" ]; then - - # Generate random (temporary) file to store install commands for needed services - RANDOM_NUM=$(shuf -i 100000-999999 -n 1) - SERVICES_FILE="services_$RANDOM_NUM.txt" - TEMP_SERVICES_FILE_PATH="/root/bin/services/$SERVICES_FILE" - touch "$TEMP_SERVICES_FILE_PATH" - - appendService - while [ "${SERVICE^^}" != "" ] || [ ! -z "$SERVICES" ]; do - if [ -z "$SERVICES" ]; then - appendService - else - if [ ! -z "$CUSTOM_SERVICES" ]; then # assumes both services and custom services passed as ENV vars - appendCustomService - else # custom services not passed as ENV var, so must prompt the user for their custom services - setUpService - while [ "${SETUP_CUSTOM_SERVICE_INSTALLATION^^}" == "Y" ]; do - appendCustomService - setUpService - done - fi - break - fi - done -fi - -# Used for updating container services in GH Actions - -UPDATING_CONTAINER="$1" -if [ "$UPDATING_CONTAINER" == "true" ]; then - cat "$TEMP_SERVICES_FILE_PATH" - rm -rf "$TEMP_SERVICES_FILE_PATH" -fi \ No newline at end of file diff --git a/container-creation/deployment-scripts/gatherSetupCommands.sh b/container-creation/deployment-scripts/gatherSetupCommands.sh deleted file mode 100644 index 6ff40ecb..00000000 --- a/container-creation/deployment-scripts/gatherSetupCommands.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# This function gathers start up commands, such as build, install, and start, for both single and multiple component applications -# Last Modified by Maxwell Klema on July 15th, 2025 -# --------------------------------------------- - -gatherSetupCommands() { - - TYPE="$1" - PROMPT="$2" - TYPE_COMMAND="${TYPE}_COMMAND" - TYPE_COMMAND="${!TYPE_COMMAND}" # get value stored by TYPE_COMMAND - declare "COMMANDS_DICT={}" - - if [ "${MULTI_COMPONENT^^}" == "Y" ]; then - if [ ! -z "$TYPE_COMMAND" ]; then # Environment Variable Passed - if echo "$TYPE_COMMAND" | jq -e > /dev/null 2>&1; then # Valid JSON - for key in $(echo "$TYPE_COMMAND" | jq -r 'keys[]'); do - gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" "$key" - addComponent "$key" - done - else - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Your \"$TYPE_COMMAND\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in $TYPE_COMMAND (GH_ACTION mode)" - exit 10 - fi - echo "⚠️ Your \"$TYPE_COMMAND\" is not valid JSON. Please re-format and try again." - writeLog "Invalid JSON in $TYPE_COMMAND" - exit 10 - fi - else # No Environment Variable Passed - gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" - while [ "$COMPONENT_PATH" != "" ]; do - addComponent "$COMPONENT_PATH" - read -p "$PROMPT" COMMAND - - # Append Component:Command k:v pair to map - COMMANDS_DICT=$(echo "$COMMANDS_DICT" | jq --arg k "$COMPONENT_PATH" --arg v "$COMMAND" '. + {($k): $v}') - gatherComponentDir "Enter the path of your component to enter the ${TYPE,,} command" - done - TYPE_COMMAND=$COMMANDS_DICT - fi - else - if [ -z "$TYPE_COMMAND" ]; then - read -p "$PROMPT" TYPE_COMMAND - fi - fi - - # Write to correct command variable - if [ "$TYPE" == "BUILD" ]; then - BUILD_COMMAND=$TYPE_COMMAND - elif [ "$TYPE" == "INSTALL" ]; then - INSTALL_COMMAND=$TYPE_COMMAND - else - START_COMMAND=$TYPE_COMMAND - fi -} \ No newline at end of file diff --git a/container-creation/get-deployment-details.sh b/container-creation/get-deployment-details.sh deleted file mode 100755 index 992cd256..00000000 --- a/container-creation/get-deployment-details.sh +++ /dev/null @@ -1,222 +0,0 @@ -#!/bin/bash -# Helper script to gather project details for automatic deployment -# Modified August 5th, 2025 by Maxwell Klema -# ------------------------------------------ - -# Define color variables (works on both light and dark backgrounds) -RESET="\033[0m" -BOLD="\033[1m" -MAGENTA='\033[35m' - -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" -echo -e "${BOLD}${MAGENTA}🌐 Let's Get Your Project Automatically Deployed ${RESET}" -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - -writeLog "Starting deploy application script" - -# Get and validate project repository ======== - -if [ -z "$PROJECT_REPOSITORY" ]; then - read -p "🚀 Paste the link to your project repository → " PROJECT_REPOSITORY - writeLog "Prompted for project repository" -fi - -CheckRepository() { - PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY#*github.com/} - PROJECT_REPOSITORY_SHORTENED=${PROJECT_REPOSITORY_SHORTENED%.git} - REPOSITORY_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED) - writeLog "Checking repository existence for $PROJECT_REPOSITORY_SHORTENED" -} - -CheckRepository - -while [ "$REPOSITORY_EXISTS" != "200" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Repository Link. Make sure your repository is private." - writeLog "Invalid repository link entered: $PROJECT_REPOSITORY (GH_ACTION mode)" - exit 10 - fi - echo "⚠️ The repository link you provided, \"$PROJECT_REPOSITORY\" was not valid." - writeLog "Invalid repository link entered: $PROJECT_REPOSITORY" - read -p "🚀 Paste the link to your project repository → " PROJECT_REPOSITORY - CheckRepository -done - -writeLog "Repository validated: $PROJECT_REPOSITORY" - -# Get Repository Branch ======== - -if [ -z "$PROJECT_BRANCH" ]; then - read -p "🪾 Enter the project branch to deploy from (leave blank for \"main\") → " PROJECT_BRANCH - writeLog "Prompted for project branch" -fi - -if [ -z "$PROJECT_BRANCH" ]; then - PROJECT_BRANCH="main" - writeLog "Using default branch: main" -fi - -REPOSITORY_BRANCH_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED/tree/$PROJECT_BRANCH) -writeLog "Checking branch existence for $PROJECT_BRANCH" - -while [ "$REPOSITORY_BRANCH_EXISTS" != "200" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Branch. Make sure your branch exists on the repository." - writeLog "Invalid branch entered: $PROJECT_BRANCH (GH_ACTION mode)" - exit 11 - fi - echo "⚠️ The branch you provided, \"$PROJECT_BRANCH\", does not exist on repository at \"$PROJECT_REPOSITORY\"." - writeLog "Invalid branch entered: $PROJECT_BRANCH" - read -p "🪾 Enter the project branch to deploy from (leave blank for \"main\") → " PROJECT_BRANCH - if [ -z "$PROJECT_BRANCH" ]; then - PROJECT_BRANCH="main" - fi - REPOSITORY_BRANCH_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" https://github.com/$PROJECT_REPOSITORY_SHORTENED/tree/$PROJECT_BRANCH) -done - -writeLog "Branch validated: $PROJECT_BRANCH" - -# Get Project Root Directory ======== - -if [ -z "$PROJECT_ROOT" ]; then - read -p "📁 Enter the project root directory (relative to repository root directory, or leave blank for root directory) → " PROJECT_ROOT - writeLog "Prompted for project root directory" -fi - -VALID_PROJECT_ROOT=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$PROJECT_ROOT") -writeLog "Validating project root directory: $PROJECT_ROOT" - -while [ "$VALID_PROJECT_ROOT" == "false" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Project Root Directory. Make sure your directory exists on the repository." - writeLog "Invalid project root directory entered: $PROJECT_ROOT (GH_ACTION mode)" - exit 12 - fi - echo "⚠️ The root directory you provided, \"$PROJECT_ROOT\", does not exist on branch, \"$PROJECT_BRANCH\", on repository at \"$PROJECT_REPOSITORY\"." - writeLog "Invalid project root directory entered: $PROJECT_ROOT" - read -p "📁 Enter the project root directory (relative to repository root directory, or leave blank for root directory) → " PROJECT_ROOT - VALID_PROJECT_ROOT=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$PROJECT_ROOT") -done - -writeLog "Project root directory validated: $PROJECT_ROOT" - -# Remove forward slash -if [[ "$PROJECT_ROOT" == "/*" ]]; then - PROJECT_ROOT="${PROJECT_ROOT:1}" -fi - -# Check if the App has multiple components (backend, frontend, multiple servers, etc.) ======== - -if [ -z "$MULTI_COMPONENT" ]; then - read -p "🔗 Does your app consist of multiple components that run independently, i.e. seperate frontend and backend (y/n) → " MULTI_COMPONENT - writeLog "Prompted for multi-component option" -fi - -while [ "${MULTI_COMPONENT^^}" != "Y" ] && [ "${MULTI_COMPONENT^^}" != "N" ] && [ "${MULTI_COMPONENT^^}" != "" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid option for MULTI_COMPONENT. It must be 'y' or 'n'. Please try again." - writeLog "Invalid multi-component option entered: $MULTI_COMPONENT (GH_ACTION mode)" - exit 13 - fi - echo "⚠️ Invalid option. Please try again." - writeLog "Invalid multi-component option entered: $MULTI_COMPONENT" - read -p "🔗 Does your app consist of multiple components that run independently, i.e. seperate frontend and backend (y/n) → " MULTI_COMPONENT -done - -if [ "${GH_ACTION^^}" == "Y" ]; then - if [ ! -z "$RUNTIME_LANGUAGE" ] && echo "$RUNTIME_LANGUAGE" | jq . >/dev/null 2>&1; then # If RUNTIME_LANGUAGE is set and is valid JSON - MULTI_COMPONENT="Y" - fi -fi - -writeLog "Multi-component option set to: $MULTI_COMPONENT" - -# Gather Deployment Commands ======== - -# Helper functions to gather and validate component directory -gatherComponentDir() { - - COMPONENT_PATH="$2" - if [ -z "$COMPONENT_PATH" ]; then - read -p "$1, relative to project root directory (To Continue, Press Enter) → " COMPONENT_PATH - writeLog "Prompted for component directory: $1" - fi - # Check that component path is valid - VALID_COMPONENT_PATH=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$COMPONENT_PATH") - writeLog "Validating component path: $COMPONENT_PATH" - - while [ "$VALID_COMPONENT_PATH" == "false" ] && [ "$COMPONENT_PATH" != "" ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Component Path: \"$COMPONENT_PATH\". Make sure your path exists on the repository." - writeLog "Invalid component path entered: $COMPONENT_PATH (GH_ACTION mode)" - exit 14 - fi - echo "⚠️ The component path you entered, \"$COMPONENT_PATH\", does not exist on branch, \"$PROJECT_BRANCH\", on repository at \"$PROJECT_REPOSITORY\"." - writeLog "Invalid component path entered: $COMPONENT_PATH" - if [ -z "$2" ]; then - read -p "$1, relative to project root directory (To Continue, Press Enter) → " COMPONENT_PATH - VALID_COMPONENT_PATH=$(node /root/bin/js/runner.js authenticateRepo "$PROJECT_REPOSITORY" "$PROJECT_BRANCH" "$COMPONENT_PATH") - else - exit 14 - fi - done - - if [[ "$COMPONENT_PATH" == /* ]]; then - COMPONENT_PATH="${COMPONENT_PATH:1}" # remove leading slash - fi - - if [ "$COMPONENT_PATH" != "" ]; then - writeLog "Component path validated: $COMPONENT_PATH" - fi -} - -UNIQUE_COMPONENTS=() - -# Helper function to add a component to unique components if its not already present -addComponent() { - COMPONENT="$1" - for CURRENT in "${UNIQUE_COMPONENTS[@]}"; do - if [ "${COMPONENT,,}" == "${CURRENT,,}" ]; then - return 0 - fi - done - UNIQUE_COMPONENTS+=("$COMPONENT") - writeLog "Added component: $COMPONENT" -} - -writeLog "Sourcing setup commands script" -source /root/bin/deployment-scripts/gatherSetupCommands.sh # Function to gather build, install, and start commands - -writeLog "Sourcing environment variables script" -source /root/bin/deployment-scripts/gatherEnvVars.sh # Gather Environment Variables - -writeLog "Gathering build commands" -gatherSetupCommands "BUILD" "🏗️ Enter the build command (leave blank if no build command) → " # Gather Build Command(s) - -writeLog "Gathering install commands" -gatherSetupCommands "INSTALL" "📦 Enter the install command (e.g., 'npm install') → " # Gather Install Command(s) - -writeLog "Gathering start commands" -gatherSetupCommands "START" "🚦 Enter the start command (e.g., 'npm start', 'python app.py') → " # Gather Start Command(s) - -if [ "${MULTI_COMPONENT^^}" == "Y" ]; then - if [ -z "$ROOT_START_COMMAND" ]; then - read -p "📍 If your container requires a start command at the root directory, i.e. Docker run, enter it here (leave blank for no command) → " ROOT_START_COMMAND - writeLog "Prompted for root start command" - fi - if [ "$ROOT_START_COMMAND" != "" ]; then - writeLog "Root start command set: $ROOT_START_COMMAND" - fi -fi - -# Get Runtime Language ======== - -writeLog "Sourcing runtime languages script" -source /root/bin/deployment-scripts/gatherRuntimeLangs.sh - -# Get Services ======== -writeLog "Sourcing services script" -source /root/bin/deployment-scripts/gatherServices.sh - -writeLog "Deployment process finished successfully" -echo -e "\n✅ Deployment Process Finished.\n" \ No newline at end of file diff --git a/container-creation/get-lxc-container-details.sh b/container-creation/get-lxc-container-details.sh deleted file mode 100644 index 5e4ee9b6..00000000 --- a/container-creation/get-lxc-container-details.sh +++ /dev/null @@ -1,332 +0,0 @@ -#!/bin/bash -# Main Container Creation Script -# Modified July 28th, 2025 by Maxwell Klema -# ------------------------------------------ - -LOG_FILE="/var/log/create-container.log" - -writeLog() { - echo "[$(date +'%Y-%m-%d %H:%M:%S')]: $1" >> "$LOG_FILE" -} - -# Define color variables (works on both light and dark backgrounds) -RESET="\033[0m" -BOLD="\033[1m" -MAGENTA='\033[35m' - -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" -echo -e "${BOLD}${MAGENTA}📦 MIE Container Creation Script ${RESET}" -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - -# Authenticate User (Only Valid Users can Create Containers) - -outputError() { - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - echo -e "${BOLD}${MAGENTA}❌ Script Failed. Exiting... ${RESET}" - echo -e "$1" - echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" -} - -writeLog "Starting Container Creation Script" - -if [ -z "$PROXMOX_USERNAME" ]; then - read -p "Enter Proxmox Username → " PROXMOX_USERNAME -fi - -if [ -z "$PROXMOX_PASSWORD" ]; then - read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD - echo "" -fi - -USER_AUTHENTICATED=$(node /root/bin/js/runner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD") - -while [ $USER_AUTHENTICATED == 'false' ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Proxmox Credentials." - writeLog "Invalid Proxmox credentials entered for user: $PROXMOX_USERNAME (GH_ACTION mode)" - exit 2 - fi - echo "❌ Authentication Failed. Try Again" - writeLog "Invalid Proxmox credentials entered for user: $PROXMOX_USERNAME" - read -p "Enter Proxmox Username → " PROXMOX_USERNAME - read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD - echo "" - - USER_AUTHENTICATED=$(node /root/bin/js/runner.js authenticateUser "$PROXMOX_USERNAME" "$PROXMOX_PASSWORD") -done - -echo "🎉 Your proxmox account, $PROXMOX_USERNAME@pve, has been authenticated" - -# Gather Container Hostname (hostname.opensource.mieweb.org) ===== - -if [ -z "$CONTAINER_NAME" ]; then - read -p "Enter Application Name (One-Word) → " CONTAINER_NAME -fi - -CONTAINER_NAME="${CONTAINER_NAME,,}" #convert to lowercase -HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}") - -while [[ $HOST_NAME_EXISTS == 'true' ]] || ! [[ "$CONTAINER_NAME" =~ ^[A-Za-z0-9-]+$ ]]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Container Hostname." - writeLog "Invalid container hostname entered: $CONTAINER_NAME (GH_ACTION mode)" - exit 3 - fi - echo "Sorry! Either that name has already been registered or your hostname is ill-formatted. Try another name" - writeLog "Invalid container hostname entered: $CONTAINER_NAME (already exists or ill-formatted)" - read -p "Enter Application Name (One-Word) → " CONTAINER_NAME - HOST_NAME_EXISTS=$(ssh root@10.15.20.69 "node /etc/nginx/checkHostnameRunner.js checkHostnameExists ${CONTAINER_NAME}") - CONTAINER_NAME="${CONTAINER_NAME,,}" -done - -echo "✅ $CONTAINER_NAME is available" - -# Choose Linux Distribution - -if [ -z "$LINUX_DISTRIBUTION" ]; then - echo "🐧 Available Linux Distributions:" - echo "1. Debian 12 (Bookworm)" - echo "2. Rocky 9 " - read -p "➡️ Choose a Linux Distribution (debian/rocky) → " LINUX_DISTRIBUTION -fi - -if [ "${LINUX_DISTRIBUTION,,}" != "debian" ] && [ "${LINUX_DISTRIBUTION,,}" != "rocky" ]; then - LINUX_DISTRIBUTION="debian" -fi - -LINUX_DISTRIBUTION=${LINUX_DISTRIBUTION,,} - -# Attempt to detect public keys - -echo -e "\n🔑 Attempting to Detect SSH Public Key..." - -AUTHORIZED_KEYS="/root/.ssh/authorized_keys" -RANDOM_NUM=$(shuf -i 100000-999999 -n 1) -PUB_FILE="key_$RANDOM_NUM.pub" -TEMP_PUB_FILE="/root/bin/ssh/temp_pubs/$PUB_FILE" # in case two users are running this script at the same time, they do not overwrite each other's temp files -touch "$TEMP_PUB_FILE" -DETECT_PUBLIC_KEY=$(sudo /root/bin/ssh/detectPublicKey.sh "$SSH_KEY_FP" "$TEMP_PUB_FILE") - -if [ "$DETECT_PUBLIC_KEY" == "Public key found for create-container" ]; then - echo "🔐 Public Key Found!" -else - echo "🔍 Could not detect Public Key" - - if [ -z "$PUBLIC_KEY" ]; then - read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY - fi - - # Check if key is valid - - while [[ "$PUBLIC_KEY" != "" && $(echo "$PUBLIC_KEY" | ssh-keygen -l -f - 2>&1 | tr -d '\r') == "(stdin) is not a public key file." ]]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid Public Key" - writeLog "Invalid public key entered (GH_ACTION mode)" - exit 5 - fi - echo "❌ \"$PUBLIC_KEY\" is not a valid key. Enter either a valid key or leave blank to skip." - writeLog "Invalid public key entered: $PUBLIC_KEY" - read -p "Enter Public Key (Allows Easy Access to Container) [OPTIONAL - LEAVE BLANK TO SKIP] → " PUBLIC_KEY - done - - if [ "$PUBLIC_KEY" != "" ]; then - echo "$PUBLIC_KEY" > "$AUTHORIZED_KEYS" && systemctl restart ssh - echo "$PUBLIC_KEY" > "$TEMP_PUB_FILE" - sudo /root/bin/ssh/publicKeyAppendJumpHost.sh "$PUBLIC_KEY" - fi -fi - -# Get HTTP Port Container Listens On - -if [ -z "$HTTP_PORT" ]; then - read -p "Enter HTTP Port for your container to listen on (80-60000) → " HTTP_PORT - if [ "${GH_ACTION^^}" == "Y" ]; then - HTTP_PORT="3000" # Default to 3000 if not set - fi -fi - -while ! [[ "$HTTP_PORT" =~ ^[0-9]+$ ]] || [ "$HTTP_PORT" -lt 80 ] || [ "$HTTP_PORT" -gt 60000 ]; do - if [ "${GH_ACTION^^}" == "Y" ]; then - outputError "Invalid HTTP Port. Must be between 80 and 60,000." - writeLog "Invalid HTTP port entered: $HTTP_PORT (GH_ACTION mode)" - exit 6 - fi - echo "❌ Invalid HTTP Port. It must be a number between 80 and 60,000." - writeLog "Invalid HTTP port entered: $HTTP_PORT" - read -p "Enter HTTP Port for your container to listen on (80-60000) → " HTTP_PORT -done - -echo "✅ HTTP Port is set to $HTTP_PORT" - -# Get any other protocols - -protocol_duplicate() { - PROTOCOL="$1" - shift #remaining params are part of list - LIST="$@" - - for item in $LIST; do - if [[ "$item" == "$PROTOCOL" ]]; then - return 0 # Protocol is a duplicate - fi - done - return 1 # Protocol is not a duplicate -} - -show_available_protocols() { - echo "" - echo "📋 Available Protocols:" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - # Display protocols in a more readable format showing abbreviation, port, and type - while read line; do - protocol_abbrev=$(echo "$line" | awk '{print $1}') - protocol_port=$(echo "$line" | awk '{print $2}') - protocol_type=$(echo "$line" | awk '{print $3}') - printf "%-12s Port %-6s (%s)\n" "$protocol_abbrev" "$protocol_port" "$protocol_type" - done < "/root/bin/protocols/master_protocol_list.txt" - echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - echo "" -} - -read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS -while [ "${USE_OTHER_PROTOCOLS^^}" != "Y" ] && [ "${USE_OTHER_PROTOCOLS^^}" != "N" ] && [ "${USER_OTHER_PROTOCOLS^^}" != "" ]; do - echo "Please answer 'y' for yes or 'n' for no." - read -p "Does your Container require any protocols other than SSH and HTTP? (y/n) → " USE_OTHER_PROTOCOLS -done - -if [ "${USE_OTHER_PROTOCOLS^^}" == "Y" ]; then - - RANDOM_NUM=$(shuf -i 100000-999999 -n 1) - PROTOCOL_BASE_FILE="protocol_list_$RANDOM_NUM.txt" - PROTOCOL_FILE="/root/bin/protocols/$PROTOCOL_BASE_FILE" - touch "$PROTOCOL_FILE" - - LIST_PROTOCOLS=() - read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"list\" to see available protocols or \"e\" to exit → " PROTOCOL_NAME - while [ "${PROTOCOL_NAME^^}" != "E" ]; do - if [ "${PROTOCOL_NAME^^}" == "LIST" ]; then - show_available_protocols - read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"list\" to see available protocols or \"e\" to exit → " PROTOCOL_NAME - continue - fi - FOUND=0 #keep track if protocol was found - while read line; do - PROTOCOL_ABBRV=$(echo "$line" | awk '{print $1}') - protocol_duplicate "$PROTOCOL_ABBRV" "${LIST_PROTOCOLS[@]}" - IS_PROTOCOL_DUPLICATE=$? - if [[ "$PROTOCOL_ABBRV" == "${PROTOCOL_NAME^^}" && "$IS_PROTOCOL_DUPLICATE" -eq 1 ]]; then - LIST_PROTOCOLS+=("$PROTOCOL_ABBRV") - PROTOCOL_UNDRLYING_NAME=$(echo "$line" | awk '{print $3}') - PROTOCOL_DEFAULT_PORT=$(echo "$line" | awk '{print $2}') - echo "$PROTOCOL_ABBRV $PROTOCOL_UNDRLYING_NAME $PROTOCOL_DEFAULT_PORT" >> "$PROTOCOL_FILE" - echo "✅ Protocol ${PROTOCOL_NAME^^} added to container." - FOUND=1 #protocol was found - break - else - echo "❌ Protocol ${PROTOCOL_NAME^^} was already added to your container. Please try again." - FOUND=2 #protocol was a duplicate - break - fi - done < <(cat "/root/bin/protocols/master_protocol_list.txt" | grep "^${PROTOCOL_NAME^^}") - - if [ $FOUND -eq 0 ]; then #if no results found, let user know. - echo "❌ Protocol ${PROTOCOL_NAME^^} not found. Please try again." - fi - - read -p "Enter the protocol abbreviation (e.g, LDAP for Lightweight Directory Access Protocol). Type \"list\" to see available protocols or \"e\" to exit → " PROTOCOL_NAME - done -fi - -# Attempt to deploy application on start. - -if [ -z "$DEPLOY_ON_START" ]; then - read -p "🚀 Do you want to deploy your project automatically? (y/n) → " DEPLOY_ON_START -fi - -while [ "${DEPLOY_ON_START^^}" != "Y" ] && [ "${DEPLOY_ON_START^^}" != "N" ] && [ "${DEPLOY_ON_START^^}" != "" ]; do - echo "Please answer 'y' for yes or 'n' for no." - read -p "🚀 Do you want to deploy your project automatically? (y/n) → " DEPLOY_ON_START -done - -if [ "${GH_ACTION^^}" == "Y" ]; then - if [ ! -z "${RUNTIME_LANGUAGE^^}" ]; then - DEPLOY_ON_START="Y" - fi -fi - -if [ "${DEPLOY_ON_START^^}" == "Y" ]; then - source /root/bin/deploy-application.sh -fi - -# send public key, port mapping, env vars, and services to hypervisor - -send_file_to_hypervisor() { - local LOCAL_FILE="$1" - local REMOTE_FOLDER="$2" - if [ "$REMOTE_FOLDER" != "container-env-vars" ]; then - if [ -s "$LOCAL_FILE" ]; then - sftp root@10.15.0.4 < /dev/null -put $LOCAL_FILE /var/lib/vz/snippets/$REMOTE_FOLDER/ -EOF - fi - else - if [ -d "$LOCAL_FILE" ]; then - sftp root@10.15.0.4 < /dev/null -put -r $LOCAL_FILE /var/lib/vz/snippets/$REMOTE_FOLDER/ -EOF - else - ENV_FOLDER="null" - fi - fi -} - -send_file_to_hypervisor "$TEMP_PUB_FILE" "container-public-keys" -send_file_to_hypervisor "$PROTOCOL_FILE" "container-port-maps" -send_file_to_hypervisor "$ENV_FOLDER_PATH" "container-env-vars" -send_file_to_hypervisor "$TEMP_SERVICES_FILE_PATH" "container-services" - -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" -echo -e "${BOLD}${MAGENTA}🚀 Starting Container Creation...${RESET}" -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" - -# Encode JSON variables -INSTALL_COMMAND_B64=$(echo -n "$INSTALL_COMMAND" | base64) -BUILD_COMMAND_B64=$(echo -n "$BUILD_COMMAND" | base64) -RUNTIME_LANGUAGE_B64=$(echo -n "$RUNTIME_LANGUAGE" | base64) -START_COMMAND_B64=$(echo -n "$START_COMMAND" | base64) - -REMOTE_CMD=( -/var/lib/vz/snippets/create-container.sh -"$CONTAINER_NAME" -"$GH_ACTION" -"$HTTP_PORT" -"$PROXMOX_USERNAME" -"$PUB_FILE" -"$PROTOCOL_BASE_FILE" -"$DEPLOY_ON_START" -"$PROJECT_REPOSITORY" -"$PROJECT_BRANCH" -"$PROJECT_ROOT" -"$INSTALL_COMMAND_B64" -"$BUILD_COMMAND_B64" -"$START_COMMAND_B64" -"$RUNTIME_LANGUAGE_B64" -"$ENV_FOLDER" -"$SERVICES_FILE" -"$LINUX_DISTRIBUTION" -"$MULTI_COMPONENT" -"$ROOT_START_COMMAND" -) - -QUOTED_REMOTE_CMD=$(printf ' %q' "${REMOTE_CMD[@]}") - -ssh -t root@10.15.0.4 "bash -c \"$QUOTED_REMOTE_CMD\"" - -rm -rf "$PROTOCOL_FILE" -rm -rf "$TEMP_PUB_FILE" -rm -rf "$TEMP_SERVICES_FILE_PATH" -rm -rf "$ENV_FOLDER_PATH" - -unset CONFIRM_PASSWORD -unset PUBLIC_KEY -unset PROXMOX_PASSWORD diff --git a/container-creation/helper-scripts/configureLDAP.sh b/container-creation/helper-scripts/configureLDAP.sh deleted file mode 100644 index bf8ec3fa..00000000 --- a/container-creation/helper-scripts/configureLDAP.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -# Script to connect a container to the LDAP server via SSSD -# Last Modified by Carter Myers on Aug 28th, 2025 -# ----------------------------------------------------- - -run_pct_exec() { - local ctid="$1" - shift - if [ "${AI_CONTAINER^^}" == "Y" ]; then - # Use printf %q to safely quote all arguments for the remote shell - local remote_cmd - printf -v remote_cmd '%q ' "$@" - ssh root@10.15.0.6 "pct exec $ctid -- $remote_cmd" - else - pct exec "$ctid" -- "$@" - fi -} - -run_pct() { - # $@ = full pct command, e.g., clone, set, start, etc. - if [ "${AI_CONTAINER^^}" == "Y" ]; then - ssh root@10.15.0.6 "pct $*" - else - pct "$@" - fi -} - -# Curl Pown.sh script to install SSSD and configure LDAP -run_pct_exec $CONTAINER_ID bash -c " -cd /root && \ -curl -O https://raw.githubusercontent.com/anishapant21/pown.sh/main/pown.sh > /dev/null 2>&1 && \ -chmod +x pown.sh -" - -# Copy .env file to container (safe for SSH / AI_CONTAINER) -ENV_FILE="/var/lib/vz/snippets/.env" -ENV_CONTENT=$(<"$ENV_FILE" sed 's/["\$`]/\\&/g') # Escape special characters -run_pct_exec $CONTAINER_ID bash -c "printf '%s\n' \"$ENV_CONTENT\" > /root/.env" - -# Run the pown.sh script to configure LDAP -run_pct_exec $CONTAINER_ID bash -c "cd /root && ./pown.sh" > /dev/null 2>&1 - -# Remove ldap_tls_cert from /etc/sssd/sssd.conf -run_pct_exec $CONTAINER_ID sed -i '/ldap_tls_cacert/d' /etc/sssd/sssd.conf > /dev/null 2>&1 - -# Add TLS_REQCERT to never in ROCKY -if [ "${LINUX_DISTRO^^}" == "ROCKY" ]; then - run_pct_exec $CONTAINER_ID bash -c "echo 'TLS_REQCERT never' >> /etc/openldap/ldap.conf" > /dev/null 2>&1 - run_pct_exec $CONTAINER_ID bash -c "authselect select sssd --force" > /dev/null 2>&1 - run_pct_exec $CONTAINER_ID bash -c "systemctl restart sssd" > /dev/null 2>&1 -fi diff --git a/container-creation/helper-scripts/deployOnStart.sh b/container-creation/helper-scripts/deployOnStart.sh deleted file mode 100644 index c0c90586..00000000 --- a/container-creation/helper-scripts/deployOnStart.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/bash -# Automation Script for attempting to automatically deploy projects and services on a container -# Last Modifided by Maxwell Klema on August 16th, 2025 -# ----------------------------------------------------- - -echo "🚀 Attempting Automatic Deployment" -REPO_BASE_NAME=$(basename -s .git "$PROJECT_REPOSITORY") - -# Helper function to normalize paths by removing duplicate slashes -normalize_path() { - echo "$1" | sed 's#/\+#/#g' -} - -# Clone github repository from correct branch ==== - -pct enter $CONTAINER_ID < /dev/null 2>&1 && \ -cd /root/$REPO_BASE_NAME && \ -git checkout $PROJECT_BRANCH > /dev/null 2>&1 -else -cd /root/$REPO_BASE_NAME && git fetch > /dev/null 2>&1 && git pull > /dev/null 2>&1 && \ -git checkout $PROJECT_BRANCH > /dev/null 2>&1 -fi -EOF - -pct exec $CONTAINER_ID -- bash -c "chmod 700 ~/.bashrc" # enable full R/W/X permissions - -# Copy over ENV variables ==== - -ENV_BASE_FOLDER="/var/lib/vz/snippets/container-env-vars/${ENV_BASE_FOLDER}" - -echo "$REPO_BASE_NAME" -echo "$PROJECT_ROOT" -if [ -d "$ENV_BASE_FOLDER" ]; then - if [ "${MULTI_COMPONENTS^^}" == "Y" ]; then - for FILE in $ENV_BASE_FOLDER/*; do - FILE_BASENAME=$(basename "$FILE") - FILE_NAME="${FILE_BASENAME%.*}" - ENV_ROUTE=$(echo "$FILE_NAME" | tr '_' '/') # acts as the route to the correct folder to place .env file in. - - ENV_VARS=$(cat $ENV_BASE_FOLDER/$FILE_BASENAME) - COMPONENT_PATH=$(normalize_path "/root/$REPO_BASE_NAME/$PROJECT_ROOT/$ENV_ROUTE") - pct exec $CONTAINER_ID -- bash -c "if [ ! -f \"$COMPONENT_PATH/.env\" ]; then touch \"$COMPONENT_PATH/.env\"; fi; echo \"$ENV_VARS\" >> \"$COMPONENT_PATH/.env\"" > /dev/null 2>&1 - done - else - ENV_FOLDER_BASE_NAME=$(basename "$ENV_BASE_FOLDER") - ENV_VARS=$(cat $ENV_BASE_FOLDER/$ENV_FOLDER_BASE_NAME.txt || true) - COMPONENT_PATH=$(normalize_path "/root/$REPO_BASE_NAME/$PROJECT_ROOT") - pct exec $CONTAINER_ID -- bash -c "if [ ! -f \"$COMPONENT_PATH/.env\" ]; then touch \"$COMPONENT_PATH/.env\"; fi; echo \"$ENV_VARS\" >> \"$COMPONENT_PATH/.env\"" > /dev/null 2>&1 - fi -fi - -# Install Specific Runtime Versions (if Needed) - -echo "VERSIONS_DICT: $VERSIONS_DICT" -echo "RUNTIME_LANGUAGE: $RUNTIME_LANGUAGE" - -pct set $CONTAINER_ID --memory 4096 --swap 0 --cores 8 > /dev/null 2>&1 - -# Function to handle runtime installation -install_runtime() { - local runtime_language=$1 - local version=$2 - - if [ "${runtime_language,,}" == "nodejs" ]; then - local major=$(echo "$version" | cut -d. -f1) - local node_version_exists=$(curl -s https://nodejs.org/dist/index.json | grep "version\":\"v$major") - if [ ! -z "$node_version_exists" ]; then - source /var/lib/vz/snippets/helper-scripts/node_runtime_install.sh "$major" - else - echo "Node.js version $version ($major) is not available. Please check the version number. Using latest version instead." - fi - elif [ "${runtime_language,,}" == "python" ]; then - IFS='.' read -r -a parts <<< "$version" - - # Fill missing parts with 0 - while [ "${#parts[@]}" -lt 3 ]; do - parts+=("0") - done - - version="${parts[0]}.${parts[1]}.${parts[2]}" - local python_version_exists=$(curl -s https://www.python.org/ftp/python/ | grep "$version") - if [ ! -z "$python_version_exists" ]; then - source /var/lib/vz/snippets/helper-scripts/python_runtime_install.sh "${LINUX_DISTRO,,}" "$version" - else - echo "Python version $version is not available. Please check the version number. Using latest version instead." - fi - fi -} - -for key in $(echo "$VERSIONS_DICT" | jq -r 'keys[]'); do - if [ "$key" == "default" ] && [ "${MULTI_COMPONENT^^}" != "Y" ]; then - version=$(echo "$VERSIONS_DICT" | jq --arg k "$key" '.[$k]') - if [ "$version" != "null" ]; then - version=$(echo "$version" | sed 's/"//g') - install_runtime "$RUNTIME_LANGUAGE" "$version" - fi - else - value=$(echo "$RUNTIME_LANGUAGE" | jq --arg k "$key" '.[$k]') - value=$(echo "$value" | sed 's/"//g') - version=$(echo "$VERSIONS_DICT" | jq --arg k "$key" '.[$k]') - if [ "$version" != "null" ]; then - version=$(echo "$version" | sed 's/"//g') - install_runtime "$value" "$version" - fi - fi -done - -# Run Installation Commands ==== - -runInstallCommands() { - - RUNTIME="$1" - COMP_DIR="$2" - - # Create normalized path - WORK_DIR=$(normalize_path "/root/$REPO_BASE_NAME/$PROJECT_ROOT/$COMP_DIR") - - if [ "${RUNTIME^^}" == "NODEJS" ]; then - pct exec $CONTAINER_ID -- bash -c "cd $WORK_DIR && sudo $INSTALL_CMD" > /dev/null 2>&1 - elif [ "${RUNTIME^^}" == "PYTHON" ]; then - pct enter $CONTAINER_ID < /dev/null -cd $WORK_DIR && \ -python3 -m venv venv && source venv/bin/activate && \ -pip install --upgrade pip && \ -$INSTALL_CMD -EOF - fi -} - -if [ "${MULTI_COMPONENTS^^}" == "Y" ]; then - for COMPONENT in $(echo "$RUNTIME_LANGUAGE" | jq -r 'keys[]'); do - RUNTIME=$(echo "$RUNTIME_LANGUAGE" | jq -r --arg k "$COMPONENT" '.[$k]') #get runtime env - INSTALL_CMD=$(echo "$INSTALL_COMMAND" | jq -r --arg k "$COMPONENT" '.[$k]') #get install command - if [ "$INSTALL_CMD" != "null" ]; then - runInstallCommands "$RUNTIME" "$COMPONENT" - fi - done -else - INSTALL_CMD=$INSTALL_COMMAND - runInstallCommands "$RUNTIME_LANGUAGE" "." -fi - -# Install Services ==== - -if [ -f "/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE" ]; then - while read line; do - pct exec $CONTAINER_ID -- bash -c "$line" > /dev/null 2>&1 - done < "/var/lib/vz/snippets/container-services/$SERVICES_BASE_FILE" -fi \ No newline at end of file diff --git a/container-creation/helper-scripts/python_runtime_install.sh b/container-creation/helper-scripts/python_runtime_install.sh deleted file mode 100644 index 8680e573..00000000 --- a/container-creation/helper-scripts/python_runtime_install.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Script to install a specific Python runtime version -# Last Modified by Maxwell Klema on August 18th, 2025 -# ---------------------------------------------------------- - -if [ "$#" -ne 2 ]; then - echo "Usage: $0 " - echo "Example: $0 debian 3.8.9" - exit 1 -fi - -OS=$1 -PYTHON_VERSION=$2 -PYTHON_URL="https://www.python.org/ftp/python/$PYTHON_VERSION/Python-$PYTHON_VERSION.tgz" - -# Function to install dependencies based on OS -install_dependencies() { - case "${OS,,}" in - debian) - pct exec $CONTAINER_ID -- bash -c "sudo apt update -y && sudo apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev wget" > /dev/null 2>&1 - ;; - rocky) - pct exec $CONTAINER_ID -- bash -c "sudo dnf update -y && sudo dnf install -y gcc zlib-devel ncurses-devel gdbm-devel nss-devel openssl-devel readline-devel wget libffi-devel" > /dev/null 2>&1 - ;; - esac -} - -# Function to install Python -install_python() { - echo "⏳ Installing Python $PYTHON_VERSION... This may take a while." - pct exec "$CONTAINER_ID" -- bash -c "cd /usr/src && sudo wget $PYTHON_URL && sudo tar xzf Python-$PYTHON_VERSION.tgz && \ - cd Python-$PYTHON_VERSION && sudo ./configure --enable-optimizations && \ - sudo make -j$(nproc) && sudo make altinstall && \ - sudo ln -sfn /usr/local/bin/python${PYTHON_VERSION%.*} /usr/local/bin/python3 && \ - python3 -m ensurepip --upgrade && python3 -m pip install --upgrade pip" - echo "Python $PYTHON_VERSION installed successfully." > /dev/null 2>&1 -} - -install_dependencies -install_python \ No newline at end of file diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/.gitignore b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/.gitignore new file mode 100644 index 00000000..50f4e4e0 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/.gitignore @@ -0,0 +1,4 @@ +container-env-vars +container-port-maps +container-public-keys +container-services \ No newline at end of file diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/Wazuh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/Wazuh new file mode 120000 index 00000000..f7718ffd --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/Wazuh @@ -0,0 +1 @@ +../../../Wazuh \ No newline at end of file diff --git a/container-creation/ssh/create-container-new.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container-new.sh old mode 100644 new mode 100755 similarity index 99% rename from container-creation/ssh/create-container-new.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container-new.sh index 7890ede7..d37c5ad9 --- a/container-creation/ssh/create-container-new.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container-new.sh @@ -356,4 +356,4 @@ if [[ -n "${CMD[*]}" ]]; then tmux new-session -d -s "$CONTAINER_NAME" "$QUOTED_CMD" fi -exit 0 \ No newline at end of file +exit 0 diff --git a/container-creation/create-container.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh old mode 100644 new mode 100755 similarity index 98% rename from container-creation/create-container.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh index 66ac88c2..6697ee91 --- a/container-creation/create-container.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh @@ -72,6 +72,9 @@ LINUX_DISTRO="${17}" MULTI_COMPONENTS="${18}" ROOT_START_COMMAND="${19}" SELF_HOSTED_RUNNER="${20}" +VERSIONS_DICT=$(echo "${21}" | base64 -d) + +echo "PROJECT ROOT: \"$PROJECT_ROOT\"" # Pick the correct template to clone ===== @@ -115,7 +118,7 @@ if [ "${GH_ACTION^^}" != "Y" ] || [ "${SELF_HOSTED_RUNNER^^}" == "N" ]; then --onboot 1 > /dev/null 2>&1 pct start $CONTAINER_ID > /dev/null 2>&1 - pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role PVEVMUser > /dev/null 2>&1 + pveum aclmod /vms/$CONTAINER_ID --user "$PROXMOX_USERNAME@pve" --role VMUser2 > /dev/null 2>&1 # Get the Container IP Address and install some packages diff --git a/container-creation/helper-scripts/PVE_user_authentication.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/PVE_user_authentication.sh old mode 100644 new mode 100755 similarity index 68% rename from container-creation/helper-scripts/PVE_user_authentication.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/PVE_user_authentication.sh index 050d7b56..4d814100 --- a/container-creation/helper-scripts/PVE_user_authentication.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/PVE_user_authentication.sh @@ -6,18 +6,18 @@ # Authenticate User (Only Valid Users can Create Containers) if [ -z "$PROXMOX_USERNAME" ]; then - read -p "Enter Proxmox Username → " PROXMOX_USERNAME + read -p "Enter Proxmox Username → " PROXMOX_USERNAME fi if [ -z "$PROXMOX_PASSWORD" ]; then - read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD - echo "" + read -sp "Enter Proxmox Password → " PROXMOX_PASSWORD + echo "" fi USER_AUTHENTICATED=$(ssh root@create-container "node /root/bin/js/runner.js authenticateUser \"$PROXMOX_USERNAME\" \"$PROXMOX_PASSWORD\"") if [ "$USER_AUTHENTICATED" == "false" ]; then - outputError 1 "Your Proxmox account, $PROXMOX_USERNAME@pve, was not authenticated. Retry with valid credentials." + outputError 1 "Your Proxmox account, $PROXMOX_USERNAME@pve, was not authenticated. Retry with valid credentials." fi -echo "🎉 Your proxmox account, $PROXMOX_USERNAME@pve, has been authenticated" \ No newline at end of file +echo "🎉 Your proxmox account, $PROXMOX_USERNAME@pve, has been authenticated" diff --git a/container-creation/configureLDAP.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/configureLDAP.sh similarity index 99% rename from container-creation/configureLDAP.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/configureLDAP.sh index a9b750fd..9f601082 100755 --- a/container-creation/configureLDAP.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/configureLDAP.sh @@ -67,4 +67,4 @@ if [ "${LINUX_DISTRO^^}" == "ROCKY" ]; then run_pct_exec $CONTAINER_ID bash -c "echo 'TLS_REQCERT never' >> /etc/openldap/ldap.conf" > /dev/null 2>&1 run_pct_exec $CONTAINER_ID bash -c "authselect select sssd --force" > /dev/null 2>&1 run_pct_exec $CONTAINER_ID bash -c "systemctl restart sssd" > /dev/null 2>&1 -fi \ No newline at end of file +fi diff --git a/container-creation/helper-scripts/create-template.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/create-template.sh old mode 100644 new mode 100755 similarity index 100% rename from container-creation/helper-scripts/create-template.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/create-template.sh diff --git a/container-creation/helper-scripts/delete-runner.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/delete-runner.sh old mode 100644 new mode 100755 similarity index 100% rename from container-creation/helper-scripts/delete-runner.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/delete-runner.sh diff --git a/container-creation/deployOnStart.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/deployOnStart.sh similarity index 100% rename from container-creation/deployOnStart.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/deployOnStart.sh diff --git a/container-creation/helper-scripts/node_runtime_install.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/node_runtime_install.sh old mode 100644 new mode 100755 similarity index 99% rename from container-creation/helper-scripts/node_runtime_install.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/node_runtime_install.sh index fc96cec5..d0294da5 --- a/container-creation/helper-scripts/node_runtime_install.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/node_runtime_install.sh @@ -10,4 +10,4 @@ pct enter "$CONTAINER_ID" -- < /dev/null $PACKAGE_MANAGER remove nodejs && \ curl -fsSL https://deb.nodesource.com/setup_$MAJOR_VERSION.x | sudo -E bash - && \ $PACKAGE_MANAGER install -y nodejs -EOF \ No newline at end of file +EOF diff --git a/container-creation/python_runtime_install.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/python_runtime_install.sh similarity index 100% rename from container-creation/python_runtime_install.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/python_runtime_install.sh diff --git a/container-creation/helper-scripts/repository_status.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/repository_status.sh old mode 100644 new mode 100755 similarity index 100% rename from container-creation/helper-scripts/repository_status.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/repository_status.sh diff --git a/container-creation/helper-scripts/verify_container_ownership.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/verify_container_ownership.sh old mode 100644 new mode 100755 similarity index 98% rename from container-creation/helper-scripts/verify_container_ownership.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/verify_container_ownership.sh index 2fb215b8..a6e58068 --- a/container-creation/helper-scripts/verify_container_ownership.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/verify_container_ownership.sh @@ -21,4 +21,5 @@ fi if [ "$CONTAINER_OWNERSHIP" != "$PROXMOX_USERNAME" ] && [ "$CONTAINER_OWNERSHIP" != "null" ]; then echo "❌ You do not own the container with name \"$CONTAINER_NAME\"." - outputError 1 "You do not own the container with name \"$CONTAINER_NAME\"." \ No newline at end of file + outputError 1 "You do not own the container with name \"$CONTAINER_NAME\"." +fi \ No newline at end of file diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/opensource-server b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/opensource-server new file mode 120000 index 00000000..a8a4f8c2 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/opensource-server @@ -0,0 +1 @@ +../../.. \ No newline at end of file diff --git a/container-creation/register-container.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh old mode 100644 new mode 100755 similarity index 99% rename from container-creation/register-container.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh index a3fae91d..77f82de2 --- a/container-creation/register-container.sh +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh @@ -257,4 +257,4 @@ if [ ! -z "$ADDITIONAL_PROTOCOLS" ]; then fi # Bottom border -echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" \ No newline at end of file +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register_proxy_hook.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register_proxy_hook.sh new file mode 100755 index 00000000..093a4601 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/register_proxy_hook.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# /var/lib/vz/snippets/register_proxy_hook.sh + +# echo "DEBUG: Hook script /var/lib/vz/snippets/register_proxy_hook.sh started. Event: $2, CTID: $1" >> /tmp/hook_debug.log + +# # Hook script for container events +# case "$2" in +# post-start) +# echo "DEBUG: Calling register-container.sh for CTID: $1" >> /tmp/hook_debug.log +# /var/lib/vz/snippets/register-container.sh "$1" >> /tmp/hook_debug.log 2>&1 +# echo "DEBUG: register-container.sh finished." >> /tmp/hook_debug.log +# ;; +# *) +# echo "DEBUG: Unhandled hook event: $2 for CTID: $1" >> /tmp/hook_debug.log +# ;; +# esac +# echo "DEBUG: Hook script /var/lib/vz/snippets/register_proxy_hook.sh finished." >> /tmp/hook_debug.log \ No newline at end of file diff --git a/container-creation/start_services.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh old mode 100644 new mode 100755 similarity index 100% rename from container-creation/start_services.sh rename to container-creation/intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh diff --git a/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/test.sh b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/test.sh new file mode 100755 index 00000000..e15b1d21 --- /dev/null +++ b/container-creation/intern-phxdc-pve1/var-lib-vz-snippets/test.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Script to add a Wazuh Agent to an LXC Container +# Last Modified by Maxwell Klema on August 5th, 2025 +# -------------------------------------------------- + +RUNTIME_LANGUAGE='{"whisper-diarization": "python", "diarization-ui": "nodejs"}' +VERSIONS_DICT='{"whisper-diarization": "3.10"}' + +normalize_path() { + echo "$1" | sed 's#/\+#/#g' +} \ No newline at end of file diff --git a/container-creation/js/.gitignore b/container-creation/js/.gitignore deleted file mode 100644 index ca4d6ae5..00000000 --- a/container-creation/js/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -coverage/ -*.log -.DS_Store -.env -.env.local -.env.development.local -.env.test.local -.env.production.local \ No newline at end of file diff --git a/container-creation/js/README.md b/container-creation/js/README.md deleted file mode 100644 index 2fc7beed..00000000 --- a/container-creation/js/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# Container Creation JavaScript Module Tests - -This directory contains comprehensive tests for the JavaScript authentication modules used in the container creation process. - -## Overview - -The test suite validates the core functionality of three modules: -- `authenticateRepo.js` - Validates GitHub repository paths and directories -- `authenticateUser.js` - Authenticates users against the Proxmox VE API -- `runner.js` - Command-line interface for the authentication modules - -## Test Coverage - -All modules have **100% test coverage** including: -- ✅ **40 total tests** covering all functionality -- ✅ **100% statement coverage** -- ✅ **100% branch coverage** -- ✅ **100% function coverage** -- ✅ **100% line coverage** - -## Running Tests - -### Prerequisites -```bash -npm install -``` - -### Running Tests -```bash -# Run all tests -npm test - -# Run tests with coverage report -npm run test:coverage - -# Run tests in watch mode (for development) -npm run test:watch -``` - -## Test Structure - -### authenticateRepo.test.js -Tests for repository path validation: -- **Path validation** - Correctly rejects file paths (containing dots) -- **URL formatting** - Handles .git removal and leading slash normalization -- **HTTP response handling** - Tests success (200) and failure responses (404, 403, etc.) -- **Edge cases** - Empty paths, root paths, special characters -- **Real-world scenarios** - Known public repositories - -### authenticateUser.test.js -Tests for Proxmox user authentication: -- **Successful authentication** - Valid credentials and proper request formatting -- **Failed authentication** - Invalid credentials, network errors, timeouts -- **Edge cases** - Empty credentials, special characters, long credentials -- **HTTPS configuration** - Proper SSL verification bypass for internal network -- **Integration scenarios** - Typical Proxmox authentication flows - -### runner.test.js -Tests for the command-line interface: -- **Module structure** - File existence and imports -- **Function routing** - Correct routing to authentication functions -- **Promise handling** - Proper async result processing - -## Expected Results - -When tests are run, you should see output similar to: -``` -Test Suites: 3 passed, 3 total -Tests: 40 passed, 40 total -Snapshots: 0 total -Time: 0.668 s - ----------------------|---------|----------|---------|---------|------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------------------|---------|----------|---------|---------|------------------- -All files | 100 | 100 | 100 | 100 | - authenticateRepo.js | 100 | 100 | 100 | 100 | - authenticateUser.js | 100 | 100 | 100 | 100 | ----------------------|---------|----------|---------|---------|------------------- -``` - -## Test Implementation Details - -### Testing Framework -- **Jest** - JavaScript testing framework with built-in mocking and coverage -- **axios-mock-adapter** - HTTP request mocking for isolated unit tests - -### Mocking Strategy -Tests use mocked HTTP calls instead of real network requests to: -- Ensure tests run quickly and reliably -- Avoid dependency on external services -- Test error conditions consistently -- Maintain test isolation - -### Test Categories -1. **Unit Tests** - Test individual functions in isolation -2. **Integration Tests** - Test module interactions and CLI behavior -3. **Edge Case Tests** - Test boundary conditions and error handling -4. **Real-world Scenario Tests** - Test with realistic inputs and use cases - -## Key Functionality Tested - -### Repository Authentication (`authenticateRepo.js`) -- Validates that repository paths exist on GitHub -- Rejects file paths (only accepts directory paths) -- Properly formats GitHub tree URLs -- Handles various HTTP response codes -- Manages network errors gracefully - -### User Authentication (`authenticateUser.js`) -- Authenticates against Proxmox VE API endpoint -- Appends @pve realm to usernames -- Uses proper HTTPS configuration for internal network -- Handles authentication success/failure appropriately -- Manages API errors and network issues - -### Command Line Interface (`runner.js`) -- Routes function calls to appropriate modules -- Handles command line arguments correctly -- Processes async results and outputs them -- Maintains proper module structure - -## Contributing - -When adding new functionality to the authentication modules: - -1. **Add corresponding tests** for any new code paths -2. **Maintain 100% coverage** by testing all branches and edge cases -3. **Use mocking** for external dependencies (HTTP calls) -4. **Follow naming conventions** used in existing tests -5. **Include both positive and negative test cases** -6. **Test edge cases** and error conditions -7. **Update this documentation** if test structure changes - -## Files - -- `package.json` - Node.js dependencies and test scripts -- `authenticateRepo.test.js` - Repository authentication tests (32 tests) -- `authenticateUser.test.js` - User authentication tests (5 tests) -- `runner.test.js` - CLI interface tests (3 tests) -- `.gitignore` - Excludes node_modules and coverage reports -- `README.md` - This documentation file \ No newline at end of file diff --git a/container-creation/js/authenticateRepo.js b/container-creation/js/authenticateRepo.js deleted file mode 100644 index 37a57e7a..00000000 --- a/container-creation/js/authenticateRepo.js +++ /dev/null @@ -1,23 +0,0 @@ -const axios = require('axios'); - -function authenticateRepo(repositoryURL, branch, folderPath) { - - if (folderPath.indexOf('.') != -1) { - return Promise.resolve(false); //early exit if path points to a specific file - } - - repositoryURL = repositoryURL.replace('.git', ''); - if (folderPath.startsWith('/')) { - folderPath = folderPath.substring(1, folderPath.length); - } - fullURL = `${repositoryURL}/tree/${branch}/${folderPath}` - - const config = { - method: "get", - url: fullURL - } - - return axios.request(config).then((response) => response.status === 200).catch(() => false); -} - -module.exports = { authenticateRepo } \ No newline at end of file diff --git a/container-creation/js/authenticateRepo.test.js b/container-creation/js/authenticateRepo.test.js deleted file mode 100644 index 9a911cc0..00000000 --- a/container-creation/js/authenticateRepo.test.js +++ /dev/null @@ -1,241 +0,0 @@ -const { authenticateRepo } = require('./authenticateRepo'); -const axios = require('axios'); -const MockAdapter = require('axios-mock-adapter'); - -describe('authenticateRepo', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('path validation', () => { - test('should return false for paths pointing to specific files', async () => { - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src/file.js' - ); - expect(result).toBe(false); - }); - - test('should return false for paths with file extensions', async () => { - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'README.md' - ); - expect(result).toBe(false); - }); - - test('should process directory paths without dots', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src' - ); - expect(result).toBe(true); - }); - }); - - describe('URL formatting', () => { - test('should remove .git extension from repository URL', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo.git', - 'main', - 'src' - ); - expect(result).toBe(true); - expect(mock.history.get[0].url).toBe(expectedURL); - }); - - test('should handle repository URLs without .git extension', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src' - ); - expect(result).toBe(true); - expect(mock.history.get[0].url).toBe(expectedURL); - }); - - test('should remove leading slash from folder path', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src/components'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - '/src/components' - ); - expect(result).toBe(true); - expect(mock.history.get[0].url).toBe(expectedURL); - }); - - test('should handle folder paths without leading slash', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src/components'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src/components' - ); - expect(result).toBe(true); - expect(mock.history.get[0].url).toBe(expectedURL); - }); - }); - - describe('HTTP response handling', () => { - test('should return true for successful HTTP 200 response', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src' - ); - expect(result).toBe(true); - }); - - test('should return false for HTTP 404 response', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/nonexistent'; - mock.onGet(expectedURL).reply(404); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'nonexistent' - ); - expect(result).toBe(false); - }); - - test('should return false for HTTP 403 response', async () => { - const expectedURL = 'https://github.com/user/private-repo/tree/main/src'; - mock.onGet(expectedURL).reply(403); - - const result = await authenticateRepo( - 'https://github.com/user/private-repo', - 'main', - 'src' - ); - expect(result).toBe(false); - }); - - test('should return false for network errors', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).networkError(); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src' - ); - expect(result).toBe(false); - }); - - test('should return false for timeout errors', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/src'; - mock.onGet(expectedURL).timeout(); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - 'src' - ); - expect(result).toBe(false); - }); - }); - - describe('edge cases', () => { - test('should handle empty folder path', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - '' - ); - expect(result).toBe(true); - }); - - test('should handle root path', async () => { - const expectedURL = 'https://github.com/user/repo/tree/main/'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - '/' - ); - expect(result).toBe(true); - }); - - test('should handle different branch names', async () => { - const expectedURL = 'https://github.com/user/repo/tree/develop/src'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'develop', - 'src' - ); - expect(result).toBe(true); - }); - - test('should handle special characters in paths', async () => { - const folderPath = 'src/special-folder_name'; - const expectedURL = `https://github.com/user/repo/tree/main/${folderPath}`; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/user/repo', - 'main', - folderPath - ); - expect(result).toBe(true); - }); - }); - - describe('real-world scenarios', () => { - test('should validate existing GitHub repository path', async () => { - // Test with a known public repository - const expectedURL = 'https://github.com/mieweb/opensource-server/tree/main/container-creation'; - mock.onGet(expectedURL).reply(200); - - const result = await authenticateRepo( - 'https://github.com/mieweb/opensource-server', - 'main', - 'container-creation' - ); - expect(result).toBe(true); - }); - - test('should handle invalid repository', async () => { - const expectedURL = 'https://github.com/nonexistent/repo/tree/main/src'; - mock.onGet(expectedURL).reply(404); - - const result = await authenticateRepo( - 'https://github.com/nonexistent/repo', - 'main', - 'src' - ); - expect(result).toBe(false); - }); - }); -}); \ No newline at end of file diff --git a/container-creation/js/authenticateUser.js b/container-creation/js/authenticateUser.js deleted file mode 100644 index 36942c4f..00000000 --- a/container-creation/js/authenticateUser.js +++ /dev/null @@ -1,28 +0,0 @@ -const axios = require('axios'); -const qs = require('qs'); -const https = require('https'); - -// authenticates user, ensuring they have a valid proxmox account -function authenticateUser(username, password) { - let data = qs.stringify({ - 'username': username + "@pve", - 'password': password - }) - - let config = { - method: 'post', - url: ' https://10.15.0.4:8006/api2/json/access/ticket', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - httpsAgent: new https.Agent({ - rejectUnauthorized: false // Disable SSL verification for self-signed certificates (Only because public facing domain is resolved to nginx server internally, so have to use hypervisor IP instead of domain) - }), - data: data - }; - - return axios.request(config).then((response) => response.status === 200).catch(() => false); -} - - -module.exports = { authenticateUser }; diff --git a/container-creation/js/authenticateUser.test.js b/container-creation/js/authenticateUser.test.js deleted file mode 100644 index e6ea6130..00000000 --- a/container-creation/js/authenticateUser.test.js +++ /dev/null @@ -1,208 +0,0 @@ -const { authenticateUser } = require('./authenticateUser'); -const axios = require('axios'); -const MockAdapter = require('axios-mock-adapter'); -const qs = require('qs'); - -describe('authenticateUser', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('successful authentication', () => { - test('should return true for valid credentials', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200, { - data: { - ticket: 'PVE:username@pve:ticket', - CSRFPreventionToken: 'token' - } - }); - - const result = await authenticateUser('testuser', 'testpass'); - expect(result).toBe(true); - }); - - test('should send correct request format', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - await authenticateUser('testuser', 'testpass'); - - expect(mock.history.post).toHaveLength(1); - expect(mock.history.post[0].url).toBe(expectedURL); - expect(mock.history.post[0].headers['Content-Type']).toBe('application/x-www-form-urlencoded'); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.username).toBe('testuser@pve'); - expect(requestData.password).toBe('testpass'); - }); - - test('should append @pve realm to username', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - await authenticateUser('admin', 'password123'); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.username).toBe('admin@pve'); - }); - }); - - describe('failed authentication', () => { - test('should return false for HTTP 401 unauthorized', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(401, { - errors: { - password: 'invalid credentials' - } - }); - - const result = await authenticateUser('wronguser', 'wrongpass'); - expect(result).toBe(false); - }); - - test('should return false for HTTP 403 forbidden', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(403); - - const result = await authenticateUser('blockeduser', 'password'); - expect(result).toBe(false); - }); - - test('should return false for HTTP 500 server error', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(500); - - const result = await authenticateUser('testuser', 'testpass'); - expect(result).toBe(false); - }); - - test('should return false for network errors', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).networkError(); - - const result = await authenticateUser('testuser', 'testpass'); - expect(result).toBe(false); - }); - - test('should return false for timeout errors', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).timeout(); - - const result = await authenticateUser('testuser', 'testpass'); - expect(result).toBe(false); - }); - }); - - describe('edge cases', () => { - test('should handle empty username', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(400); - - const result = await authenticateUser('', 'password'); - expect(result).toBe(false); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.username).toBe('@pve'); - }); - - test('should handle empty password', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(401); - - const result = await authenticateUser('testuser', ''); - expect(result).toBe(false); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.password).toBe(''); - }); - - test('should handle special characters in username', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - const result = await authenticateUser('test.user-123', 'password'); - expect(result).toBe(true); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.username).toBe('test.user-123@pve'); - }); - - test('should handle special characters in password', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - const specialPassword = 'p@ssw0rd!#$%^&*()'; - const result = await authenticateUser('testuser', specialPassword); - expect(result).toBe(true); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.password).toBe(specialPassword); - }); - - test('should handle very long credentials', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - const longUsername = 'a'.repeat(100); - const longPassword = 'b'.repeat(200); - - const result = await authenticateUser(longUsername, longPassword); - expect(result).toBe(true); - - const requestData = qs.parse(mock.history.post[0].data); - expect(requestData.username).toBe(longUsername + '@pve'); - expect(requestData.password).toBe(longPassword); - }); - }); - - describe('HTTPS configuration', () => { - test('should use correct HTTPS agent configuration', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - mock.onPost(expectedURL).reply(200); - - await authenticateUser('testuser', 'testpass'); - - // Verify the request was made with HTTPS agent that ignores SSL verification - expect(mock.history.post[0].httpsAgent).toBeDefined(); - expect(mock.history.post[0].httpsAgent.options.rejectUnauthorized).toBe(false); - }); - }); - - describe('integration scenarios', () => { - test('should handle typical Proxmox user authentication flow', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - - // Mock successful Proxmox API response - mock.onPost(expectedURL).reply(200, { - data: { - ticket: 'PVE:root@pve:65F2E7FC::H+xGkJKV4gXOc2J2nqJGGFEGhOTMn9/8g+YQ4XLCFB6j', - CSRFPreventionToken: '65F2E7FC:Y5JftTJG3Q+zGUP2xGGFEGhOTMn9' - } - }); - - const result = await authenticateUser('root', 'complex_password_123'); - expect(result).toBe(true); - }); - - test('should handle invalid credentials correctly', async () => { - const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; - - // Mock Proxmox API error response for invalid credentials - mock.onPost(expectedURL).reply(401, { - errors: { - password: 'invalid user credentials' - } - }); - - const result = await authenticateUser('invaliduser', 'wrongpassword'); - expect(result).toBe(false); - }); - }); -}); \ No newline at end of file diff --git a/container-creation/js/package-lock.json b/container-creation/js/package-lock.json deleted file mode 100644 index f130c21b..00000000 --- a/container-creation/js/package-lock.json +++ /dev/null @@ -1,4061 +0,0 @@ -{ - "name": "container-creation-js", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "container-creation-js", - "version": "1.0.0", - "dependencies": { - "axios": "^1.12.1", - "qs": "^6.12.0" - }, - "devDependencies": { - "axios-mock-adapter": "^1.22.0", - "jest": "^29.7.0" - } - }, - "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": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "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": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@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.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "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": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@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": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=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": { - "node": ">=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": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "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": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "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": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "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": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "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/@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": { - "type-detect": "4.0.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": { - "@sinonjs/commons": "^3.0.0" - } - }, - "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": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "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": { - "@types/node": "*" - } - }, - "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": { - "@types/istanbul-lib-coverage": "*" - } - }, - "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": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", - "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "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": { - "@types/yargs-parser": "*" - } - }, - "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/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": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "node": ">=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": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 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": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", - "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios-mock-adapter": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", - "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "is-buffer": "^2.0.5" - }, - "peerDependencies": { - "axios": ">= 0.17.0" - } - }, - "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": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=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": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=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": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "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.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.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^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-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "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": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "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/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", - "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001737", - "electron-to-chromium": "^1.5.211", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "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-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/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "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", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001739", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz", - "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "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": { - "node": ">=10" - } - }, - "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": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "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": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "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": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "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/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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/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/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": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "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": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "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": { - "node": ">=8" - } - }, - "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/electron-to-chromium": { - "version": "1.5.214", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.214.tgz", - "integrity": "sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==", - "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": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "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/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "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", - "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/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "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": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "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": ">=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==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "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" - } - }, - "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": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "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/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": { - "bser": "2.1.1" - } - }, - "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": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=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": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "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": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "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" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "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": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-symbols": { - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/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": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "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": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "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-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "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", - "engines": { - "node": ">=4" - } - }, - "node_modules/is-core-module": { - "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": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": { - "node": ">=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": { - "node": ">=6" - } - }, - "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": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "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": { - "node": ">=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": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "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": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=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": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "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": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "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": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "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": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "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": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "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": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "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": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "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": ">=6" - } - }, - "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": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "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": { - "node": ">=6" - } - }, - "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": { - "node": ">=6" - } - }, - "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": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "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": { - "yallist": "^3.0.2" - } - }, - "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": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "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/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/micromatch": { - "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": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "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/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" - }, - "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": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "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==", - "dev": true, - "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/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.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": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "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/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": { - "wrappy": "1" - } - }, - "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": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=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": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "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/picocolors": { - "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": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=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": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "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": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/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": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "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.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-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": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=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": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "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": { - "node": ">=10" - } - }, - "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": { - "semver": "bin/semver.js" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "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": { - "es-errors": "^1.3.0", - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/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": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "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": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "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": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=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": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=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": { - "node": ">=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": { - "node": ">=6" - } - }, - "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": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=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": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "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-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": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "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": { - "node": ">=4" - } - }, - "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": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "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": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "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": { - "makeerror": "1.0.12" - } - }, - "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": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 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": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "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": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "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": { - "node": ">=10" - } - }, - "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/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": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "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": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/container-creation/js/package.json b/container-creation/js/package.json deleted file mode 100644 index 806011b5..00000000 --- a/container-creation/js/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "container-creation-js", - "version": "1.0.0", - "description": "Authentication modules for container creation", - "main": "runner.js", - "scripts": { - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage" - }, - "dependencies": { - "axios": "^1.12.1", - "qs": "^6.12.0" - }, - "devDependencies": { - "jest": "^29.7.0", - "axios-mock-adapter": "^1.22.0" - }, - "jest": { - "testEnvironment": "node", - "coverageDirectory": "coverage", - "collectCoverageFrom": [ - "*.js", - "!runner.js", - "!coverage/**" - ] - } -} \ No newline at end of file diff --git a/container-creation/js/run-tests.sh b/container-creation/js/run-tests.sh deleted file mode 100755 index 5ae1baae..00000000 --- a/container-creation/js/run-tests.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/bash - -# Test runner script for container-creation JavaScript modules -# This script provides an easy way to run different types of tests - -set -e - -echo "🧪 Container Creation JS Module Test Runner" -echo "==========================================" - -# Change to the JS directory -cd "$(dirname "$0")" - -# Check if node_modules exists -if [ ! -d "node_modules" ]; then - echo "📦 Installing dependencies..." - npm install - echo "" -fi - -# Parse command line arguments -case "${1:-all}" in - "all"|"") - echo "🚀 Running all tests..." - npm test - ;; - "coverage") - echo "📊 Running tests with coverage report..." - npm run test:coverage - ;; - "watch") - echo "👀 Running tests in watch mode..." - npm run test:watch - ;; - "repo") - echo "🔗 Running repository authentication tests..." - npx jest authenticateRepo.test.js - ;; - "user") - echo "👤 Running user authentication tests..." - npx jest authenticateUser.test.js - ;; - "runner") - echo "⚙️ Running CLI runner tests..." - npx jest runner.test.js - ;; - "help"|"-h"|"--help") - echo "Usage: $0 [test-type]" - echo "" - echo "Test types:" - echo " all Run all tests (default)" - echo " coverage Run tests with coverage report" - echo " watch Run tests in watch mode" - echo " repo Run repository authentication tests only" - echo " user Run user authentication tests only" - echo " runner Run CLI runner tests only" - echo " help Show this help message" - exit 0 - ;; - *) - echo "❌ Unknown test type: $1" - echo "Run '$0 help' for available options" - exit 1 - ;; -esac - -echo "" -echo "✅ Test run completed!" \ No newline at end of file diff --git a/container-creation/js/runner.js b/container-creation/js/runner.js deleted file mode 100644 index 8fde30cb..00000000 --- a/container-creation/js/runner.js +++ /dev/null @@ -1,13 +0,0 @@ -authenticateuser = require("./authenticateUser.js"); -authenticaterepo = require("./authenticateRepo.js") - -const [, , func, ...args] = process.argv; -if (func == "authenticateUser") { - authenticateuser.authenticateUser(...args).then((result) => { - console.log(result); - }); -} else if (func == "authenticateRepo") { - authenticaterepo.authenticateRepo(...args).then((result) => { - console.log(result); - }) -} diff --git a/container-creation/js/runner.test.js b/container-creation/js/runner.test.js deleted file mode 100644 index 354fdb0f..00000000 --- a/container-creation/js/runner.test.js +++ /dev/null @@ -1,44 +0,0 @@ -const path = require('path'); - -describe('runner.js', () => { - const runnerPath = path.join(__dirname, 'runner.js'); - - describe('module structure', () => { - test('should exist and be executable', () => { - const fs = require('fs'); - expect(fs.existsSync(runnerPath)).toBe(true); - }); - - test('should import required modules correctly', () => { - // This tests that the runner can load without syntax errors - const runnerCode = require('fs').readFileSync(runnerPath, 'utf8'); - expect(runnerCode).toContain('authenticateuser = require("./authenticateUser.js")'); - expect(runnerCode).toContain('authenticaterepo = require("./authenticateRepo.js")'); - }); - - test('should handle command line argument parsing', () => { - const runnerCode = require('fs').readFileSync(runnerPath, 'utf8'); - expect(runnerCode).toContain('const [, , func, ...args] = process.argv'); - expect(runnerCode).toContain('if (func == "authenticateUser")'); - expect(runnerCode).toContain('else if (func == "authenticateRepo")'); - }); - }); - - describe('function routing', () => { - test('should route to authenticateUser function', () => { - const runnerCode = require('fs').readFileSync(runnerPath, 'utf8'); - expect(runnerCode).toContain('authenticateuser.authenticateUser(...args)'); - }); - - test('should route to authenticateRepo function', () => { - const runnerCode = require('fs').readFileSync(runnerPath, 'utf8'); - expect(runnerCode).toContain('authenticaterepo.authenticateRepo(...args)'); - }); - - test('should handle promise resolution', () => { - const runnerCode = require('fs').readFileSync(runnerPath, 'utf8'); - expect(runnerCode).toContain('.then((result) => {'); - expect(runnerCode).toContain('console.log(result);'); - }); - }); -}); \ No newline at end of file diff --git a/container-creation/node_runtime_install.sh b/container-creation/node_runtime_install.sh deleted file mode 100644 index 21095418..00000000 --- a/container-creation/node_runtime_install.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Script to install a specific Node.js runtime version -# Last Modified by Maxwell Klema on August 18th, 2025 -# ---------------------------------------------------------- - -pct enter "$CONTAINER_ID" -- < /dev/null", - "sudo apt update -y", - "sudo apt install -y docker-ce docker-ce-cli containerd.io", - "sudo systemctl enable docker", - "sudo systemctl start docker" - ], - "rabbitmq": [ - "sudo apt update -y", - "sudo apt install -y rabbitmq-server", - "sudo systemctl enable rabbitmq-server", - "sudo systemctl start rabbitmq-server" - ], - "memcached": [ - "sudo apt update -y", - "sudo apt install -y memcached", - "sudo systemctl enable memcached", - "sudo systemctl start memcached" - ], - "mariadb": [ - "sudo apt update -y", - "sudo apt install -y mariadb-server", - "sudo systemctl enable mariadb", - "sudo systemctl start mariadb" - ] -} \ No newline at end of file diff --git a/container-creation/services/service_map_rocky.json b/container-creation/services/service_map_rocky.json deleted file mode 100644 index 1b661433..00000000 --- a/container-creation/services/service_map_rocky.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "meteor": [ - "dnf install tar -y", - "curl https://install.meteor.com/ | sh" - ], - "mongodb": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y gnupg curl", - "curl -fsSL https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor -o /etc/pki/rpm-gpg/RPM-GPG-KEY-mongodb", - "echo '[mongodb-org-7.0]' | sudo tee /etc/yum.repos.d/mongodb-org-7.0.repo", - "echo 'name=MongoDB Repository' >> /etc/yum.repos.d/mongodb-org-7.0.repo", - "echo 'baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/7.0/x86_64/' >> /etc/yum.repos.d/mongodb-org-7.0.repo", - "echo 'gpgcheck=1' >> /etc/yum.repos.d/mongodb-org-7.0.repo", - "echo 'enabled=1' >> /etc/yum.repos.d/mongodb-org-7.0.repo", - "echo 'gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mongodb' >> /etc/yum.repos.d/mongodb-org-7.0.repo", - "sudo dnf install -y mongodb-org", - "sudo systemctl enable mongod", - "sudo systemctl start mongod" - ], - "redis": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y redis", - "sudo systemctl enable redis", - "sudo systemctl start redis" - ], - "postgresql": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y postgresql-server postgresql-contrib", - "sudo postgresql-setup --initdb", - "sudo systemctl enable postgresql", - "sudo systemctl start postgresql" - ], - "apache": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y httpd", - "sudo systemctl enable httpd", - "sudo systemctl start httpd" - ], - "httpd": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y httpd", - "sudo systemctl enable httpd", - "sudo systemctl start httpd" - ], - "nginx": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y nginx", - "sudo systemctl enable nginx", - "sudo systemctl start nginx" - ], - "docker": [ - "sudo dnf update -y", - "sudo dnf install -y yum-utils device-mapper-persistent-data lvm2", - "sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo", - "sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin", - "sudo systemctl enable docker", - "sudo systemctl start docker" - ], - "rabbitmq": [ - "sudo dnf install -y epel-release", - "sudo dnf install -y erlang", - "sudo dnf update -y", - "rpm --import 'https://github.com/rabbitmq/signing-keys/releases/download/3.0/rabbitmq-release-signing-key.asc'", - "rpm --import 'https://github.com/rabbitmq/signing-keys/releases/download/3.0/cloudsmith.rabbitmq-server.9F4587F226208342.key'", - "echo '[rabbitmq-server]' | sudo tee /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'name=rabbitmq-server' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'baseurl=https://packagecloud.io/rabbitmq/rabbitmq-server/el/9/$basearch' | sudo tee /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'repo_gpgcheck=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'gpgcheck=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'enabled=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'gpgkey=https://packagecloud.io/rabbitmq/rabbitmq-server/gpgkey' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'sslverify=1' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'sslcacert=/etc/pki/tls/certs/ca-bundle.crt' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "echo 'metadata_expire=300' | sudo tee -a /etc/yum.repos.d/rabbitmq_rabbitmq-server.repo", - "sudo dnf install -y rabbitmq-server", - "sudo systemctl enable rabbitmq-server", - "sudo systemctl start rabbitmq-server" - ], - "memcached": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y memcached", - "sudo systemctl enable memcached", - "sudo systemctl start memcached" - ], - "mariadb": [ - "sudo dnf install -y epel-release", - "sudo dnf update -y", - "sudo dnf install -y mariadb-server", - "sudo systemctl enable mariadb", - "sudo systemctl start mariadb" - ] -} \ No newline at end of file diff --git a/container-creation/ssh/detectPublicKey.sh b/container-creation/ssh/detectPublicKey.sh deleted file mode 100755 index 29edf5ab..00000000 --- a/container-creation/ssh/detectPublicKey.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Detect if the user in the current session logged in via an SSH public key -# Last Updated June 26, 2025 Maxwell Klema - -USER="create-container" #Change Later -PUBLIC_KEY_LIST="/root/.ssh/authorized_keys" - - -KEY_FINGERPRINT="$1" -TEMP_PUB_FILE="$2" - -if [ "$KEY_FINGERPRINT" != "" ]; then - # Iterate over each public key, compute fingerprint, see if there is a match - - while read line; do - echo "$line" > "$TEMP_PUB_FILE" - PUB_FINGERPRINT=$(ssh-keygen -lf "$TEMP_PUB_FILE" | awk '{print $2}') - if [[ "$PUB_FINGERPRINT" == "$KEY_FINGERPRINT" ]]; then - echo "Public key found for $USER" - exit 0 - fi - done < <(tac $PUBLIC_KEY_LIST) #Iterates backwards without creating subprocess (allows exit in loop) - - echo "" > "$TEMP_PUB_FILE" -fi \ No newline at end of file diff --git a/container-creation/ssh/publicKeyAppendJumpHost.sh b/container-creation/ssh/publicKeyAppendJumpHost.sh deleted file mode 100644 index 0a92b167..00000000 --- a/container-creation/ssh/publicKeyAppendJumpHost.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# A script that appends a user's public key to the SSH jump host container to prevent them having to enter a password -# June 25th, 2025 Maxwell Klema - -PUBLIC_KEY=$1 -JUMP_HOST="10.15.0.4" #temporary until jump server ready - -# SSH into the Jump Host - -ssh root@"$JUMP_HOST" "echo '$PUBLIC_KEY' >> /home/create-container/.ssh/authorized_keys && systemctl restart ssh" - -exit 0 From e7a3c0146bffb648123c2b2713c4762820dc2b7f Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Mon, 20 Oct 2025 13:55:25 -0400 Subject: [PATCH 5/7] fix github action paths --- .github/workflows/container-creation-tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/container-creation-tests.yml b/.github/workflows/container-creation-tests.yml index f2d0e6c1..3ca2c5be 100644 --- a/.github/workflows/container-creation-tests.yml +++ b/.github/workflows/container-creation-tests.yml @@ -4,11 +4,11 @@ on: push: branches: [ main, develop ] paths: - - 'container-creation/js/**' + - 'container-creation/container-creation/root-bin/js/**' pull_request: branches: [ main, develop ] paths: - - 'container-creation/js/**' + - 'container-creation/container-creation/root-bin/js/**' jobs: test: @@ -27,25 +27,25 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - cache-dependency-path: 'container-creation/js/package-lock.json' + cache-dependency-path: 'container-creation/container-creation/root-bin/js/package-lock.json' - name: Install dependencies - working-directory: ./container-creation/js + working-directory: ./container-creation/container-creation/root-bin/js run: npm ci - name: Run tests - working-directory: ./container-creation/js + working-directory: ./container-creation/container-creation/root-bin/js run: npm test - name: Run tests with coverage - working-directory: ./container-creation/js + working-directory: ./container-creation/container-creation/root-bin/js run: npm run test:coverage - name: Upload coverage reports if: matrix.node-version == '20' uses: codecov/codecov-action@v3 with: - file: ./container-creation/js/coverage/lcov.info + file: ./container-creation/container-creation/root-bin/js/coverage/lcov.info flags: container-creation-js name: container-creation-js-coverage fail_ci_if_error: false \ No newline at end of file From 1578fc9204e5870b4f826b6734de6f288a817f31 Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Mon, 20 Oct 2025 13:59:23 -0400 Subject: [PATCH 6/7] bring package.json back from main --- .../root-bin/js/authenticateRepo.test.js | 241 ++ .../root-bin/js/authenticateUser.test.js | 208 + .../root-bin/js/package-lock.json | 3816 ++++++++++++++++- .../root-bin/js/package.json | 27 +- 4 files changed, 4065 insertions(+), 227 deletions(-) create mode 100644 container-creation/container-creation/root-bin/js/authenticateRepo.test.js create mode 100644 container-creation/container-creation/root-bin/js/authenticateUser.test.js diff --git a/container-creation/container-creation/root-bin/js/authenticateRepo.test.js b/container-creation/container-creation/root-bin/js/authenticateRepo.test.js new file mode 100644 index 00000000..9a911cc0 --- /dev/null +++ b/container-creation/container-creation/root-bin/js/authenticateRepo.test.js @@ -0,0 +1,241 @@ +const { authenticateRepo } = require('./authenticateRepo'); +const axios = require('axios'); +const MockAdapter = require('axios-mock-adapter'); + +describe('authenticateRepo', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('path validation', () => { + test('should return false for paths pointing to specific files', async () => { + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src/file.js' + ); + expect(result).toBe(false); + }); + + test('should return false for paths with file extensions', async () => { + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'README.md' + ); + expect(result).toBe(false); + }); + + test('should process directory paths without dots', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src' + ); + expect(result).toBe(true); + }); + }); + + describe('URL formatting', () => { + test('should remove .git extension from repository URL', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo.git', + 'main', + 'src' + ); + expect(result).toBe(true); + expect(mock.history.get[0].url).toBe(expectedURL); + }); + + test('should handle repository URLs without .git extension', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src' + ); + expect(result).toBe(true); + expect(mock.history.get[0].url).toBe(expectedURL); + }); + + test('should remove leading slash from folder path', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src/components'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + '/src/components' + ); + expect(result).toBe(true); + expect(mock.history.get[0].url).toBe(expectedURL); + }); + + test('should handle folder paths without leading slash', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src/components'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src/components' + ); + expect(result).toBe(true); + expect(mock.history.get[0].url).toBe(expectedURL); + }); + }); + + describe('HTTP response handling', () => { + test('should return true for successful HTTP 200 response', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src' + ); + expect(result).toBe(true); + }); + + test('should return false for HTTP 404 response', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/nonexistent'; + mock.onGet(expectedURL).reply(404); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'nonexistent' + ); + expect(result).toBe(false); + }); + + test('should return false for HTTP 403 response', async () => { + const expectedURL = 'https://github.com/user/private-repo/tree/main/src'; + mock.onGet(expectedURL).reply(403); + + const result = await authenticateRepo( + 'https://github.com/user/private-repo', + 'main', + 'src' + ); + expect(result).toBe(false); + }); + + test('should return false for network errors', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).networkError(); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src' + ); + expect(result).toBe(false); + }); + + test('should return false for timeout errors', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/src'; + mock.onGet(expectedURL).timeout(); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + 'src' + ); + expect(result).toBe(false); + }); + }); + + describe('edge cases', () => { + test('should handle empty folder path', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + '' + ); + expect(result).toBe(true); + }); + + test('should handle root path', async () => { + const expectedURL = 'https://github.com/user/repo/tree/main/'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + '/' + ); + expect(result).toBe(true); + }); + + test('should handle different branch names', async () => { + const expectedURL = 'https://github.com/user/repo/tree/develop/src'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'develop', + 'src' + ); + expect(result).toBe(true); + }); + + test('should handle special characters in paths', async () => { + const folderPath = 'src/special-folder_name'; + const expectedURL = `https://github.com/user/repo/tree/main/${folderPath}`; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/user/repo', + 'main', + folderPath + ); + expect(result).toBe(true); + }); + }); + + describe('real-world scenarios', () => { + test('should validate existing GitHub repository path', async () => { + // Test with a known public repository + const expectedURL = 'https://github.com/mieweb/opensource-server/tree/main/container-creation'; + mock.onGet(expectedURL).reply(200); + + const result = await authenticateRepo( + 'https://github.com/mieweb/opensource-server', + 'main', + 'container-creation' + ); + expect(result).toBe(true); + }); + + test('should handle invalid repository', async () => { + const expectedURL = 'https://github.com/nonexistent/repo/tree/main/src'; + mock.onGet(expectedURL).reply(404); + + const result = await authenticateRepo( + 'https://github.com/nonexistent/repo', + 'main', + 'src' + ); + expect(result).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/js/authenticateUser.test.js b/container-creation/container-creation/root-bin/js/authenticateUser.test.js new file mode 100644 index 00000000..e6ea6130 --- /dev/null +++ b/container-creation/container-creation/root-bin/js/authenticateUser.test.js @@ -0,0 +1,208 @@ +const { authenticateUser } = require('./authenticateUser'); +const axios = require('axios'); +const MockAdapter = require('axios-mock-adapter'); +const qs = require('qs'); + +describe('authenticateUser', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + describe('successful authentication', () => { + test('should return true for valid credentials', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200, { + data: { + ticket: 'PVE:username@pve:ticket', + CSRFPreventionToken: 'token' + } + }); + + const result = await authenticateUser('testuser', 'testpass'); + expect(result).toBe(true); + }); + + test('should send correct request format', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + await authenticateUser('testuser', 'testpass'); + + expect(mock.history.post).toHaveLength(1); + expect(mock.history.post[0].url).toBe(expectedURL); + expect(mock.history.post[0].headers['Content-Type']).toBe('application/x-www-form-urlencoded'); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.username).toBe('testuser@pve'); + expect(requestData.password).toBe('testpass'); + }); + + test('should append @pve realm to username', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + await authenticateUser('admin', 'password123'); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.username).toBe('admin@pve'); + }); + }); + + describe('failed authentication', () => { + test('should return false for HTTP 401 unauthorized', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(401, { + errors: { + password: 'invalid credentials' + } + }); + + const result = await authenticateUser('wronguser', 'wrongpass'); + expect(result).toBe(false); + }); + + test('should return false for HTTP 403 forbidden', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(403); + + const result = await authenticateUser('blockeduser', 'password'); + expect(result).toBe(false); + }); + + test('should return false for HTTP 500 server error', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(500); + + const result = await authenticateUser('testuser', 'testpass'); + expect(result).toBe(false); + }); + + test('should return false for network errors', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).networkError(); + + const result = await authenticateUser('testuser', 'testpass'); + expect(result).toBe(false); + }); + + test('should return false for timeout errors', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).timeout(); + + const result = await authenticateUser('testuser', 'testpass'); + expect(result).toBe(false); + }); + }); + + describe('edge cases', () => { + test('should handle empty username', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(400); + + const result = await authenticateUser('', 'password'); + expect(result).toBe(false); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.username).toBe('@pve'); + }); + + test('should handle empty password', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(401); + + const result = await authenticateUser('testuser', ''); + expect(result).toBe(false); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.password).toBe(''); + }); + + test('should handle special characters in username', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + const result = await authenticateUser('test.user-123', 'password'); + expect(result).toBe(true); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.username).toBe('test.user-123@pve'); + }); + + test('should handle special characters in password', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + const specialPassword = 'p@ssw0rd!#$%^&*()'; + const result = await authenticateUser('testuser', specialPassword); + expect(result).toBe(true); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.password).toBe(specialPassword); + }); + + test('should handle very long credentials', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + const longUsername = 'a'.repeat(100); + const longPassword = 'b'.repeat(200); + + const result = await authenticateUser(longUsername, longPassword); + expect(result).toBe(true); + + const requestData = qs.parse(mock.history.post[0].data); + expect(requestData.username).toBe(longUsername + '@pve'); + expect(requestData.password).toBe(longPassword); + }); + }); + + describe('HTTPS configuration', () => { + test('should use correct HTTPS agent configuration', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + mock.onPost(expectedURL).reply(200); + + await authenticateUser('testuser', 'testpass'); + + // Verify the request was made with HTTPS agent that ignores SSL verification + expect(mock.history.post[0].httpsAgent).toBeDefined(); + expect(mock.history.post[0].httpsAgent.options.rejectUnauthorized).toBe(false); + }); + }); + + describe('integration scenarios', () => { + test('should handle typical Proxmox user authentication flow', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + + // Mock successful Proxmox API response + mock.onPost(expectedURL).reply(200, { + data: { + ticket: 'PVE:root@pve:65F2E7FC::H+xGkJKV4gXOc2J2nqJGGFEGhOTMn9/8g+YQ4XLCFB6j', + CSRFPreventionToken: '65F2E7FC:Y5JftTJG3Q+zGUP2xGGFEGhOTMn9' + } + }); + + const result = await authenticateUser('root', 'complex_password_123'); + expect(result).toBe(true); + }); + + test('should handle invalid credentials correctly', async () => { + const expectedURL = ' https://10.15.0.4:8006/api2/json/access/ticket'; + + // Mock Proxmox API error response for invalid credentials + mock.onPost(expectedURL).reply(401, { + errors: { + password: 'invalid user credentials' + } + }); + + const result = await authenticateUser('invaliduser', 'wrongpassword'); + expect(result).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/container-creation/container-creation/root-bin/js/package-lock.json b/container-creation/container-creation/root-bin/js/package-lock.json index 43d24316..9593da07 100644 --- a/container-creation/container-creation/root-bin/js/package-lock.json +++ b/container-creation/container-creation/root-bin/js/package-lock.json @@ -1,50 +1,2981 @@ { - "name": "js", + "name": "container-creation-js", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { + "name": "container-creation-js", + "version": "1.0.0", + "dependencies": { + "axios": "^1.12.1", + "qs": "^6.12.0" + }, + "devDependencies": { + "axios-mock-adapter": "^1.22.0", + "jest": "^29.7.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@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, + "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.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "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, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@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, + "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, + "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-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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 + }, + "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, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=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, + "engines": { + "node": ">=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, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "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, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "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, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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 + }, + "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, + "dependencies": { + "type-detect": "4.0.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, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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, + "dependencies": { + "@types/node": "*" + } + }, + "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 + }, + "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, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "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, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.0.tgz", + "integrity": "sha512-MKNwXh3seSK8WurXF7erHPJ2AONmMwkI7zAMrXZDPIru8jRqkk6rGDBVbw4mLwfqA+ZZliiDPg05JQ3uW66tKQ==", + "dev": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "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 + }, + "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, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "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 + }, + "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, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 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, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios-mock-adapter": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz", + "integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "is-buffer": "^2.0.5" + }, + "peerDependencies": { + "axios": ">= 0.17.0" + } + }, + "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, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=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, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=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, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^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.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^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-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "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, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.18.tgz", + "integrity": "sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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 + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "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", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + } + }, + "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": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "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 + }, + "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, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "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, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "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/electron-to-chromium": { + "version": "1.5.237", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.237.tgz", + "integrity": "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==", + "dev": true + }, + "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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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 + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "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, + "engines": { + "node": ">=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==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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" + } + }, + "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, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "bser": "2.1.1" + } + }, + "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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=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, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "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", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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 + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=10.17.0" + } + }, + "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, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "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" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-core-module": { + "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, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=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, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=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, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=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, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "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, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "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, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "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, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "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, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "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, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, "dependencies": { - "axios": "^1.10.0", - "https": "^1.0.0", - "qs": "^6.14.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "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, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/axios": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", - "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "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, "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "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, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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 + }, + "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, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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 + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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 + }, + "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, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/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, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", "engines": { "node": ">= 0.4" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "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 + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.25", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.25.tgz", + "integrity": "sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==", + "dev": true + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "path-key": "^3.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -52,136 +2983,361 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { - "delayed-stream": "~1.0.0" + "wrappy": "1" + } + }, + "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, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "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, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "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, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-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 + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=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, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "license": "MIT" + }, + "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": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.14.0", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "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 + }, + "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, "engines": { - "node": ">=0.4.0" + "node": ">=0.10.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==", + "node_modules/resolve": { + "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, "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.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/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "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, + "dependencies": { + "resolve-from": "^5.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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==", + "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, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "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==", - "dependencies": { - "es-errors": "^1.3.0" - }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">=8" } }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "node_modules/side-channel": { + "version": "1.1.0", + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "es-errors": "^1.3.0", + "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": ">= 6" - } - }, - "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==", + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/side-channel-list": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -190,22 +3346,32 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-proto": { + "node_modules/side-channel-map": { "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" + "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/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "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" }, @@ -213,78 +3379,150 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "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 + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "escape-string-regexp": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "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, "dependencies": { - "has-symbols": "^1.0.3" + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + } + }, + "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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { - "function-bind": "^1.1.2" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==" + "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, + "engines": { + "node": ">=8" + } }, - "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==", + "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, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "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==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "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, "engines": { "node": ">= 0.4" }, @@ -292,91 +3530,221 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "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, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "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 + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { - "side-channel": "^1.1.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=0.6" + "node": ">=8.0" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "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, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "update-browserslist-db": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "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==", + "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, "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10.12.0" + } + }, + "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, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "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==", + "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, "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/side-channel-weakmap": { + "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "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, "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" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, "engines": { - "node": ">= 0.4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/container-creation/container-creation/root-bin/js/package.json b/container-creation/container-creation/root-bin/js/package.json index 64c6916b..806011b5 100644 --- a/container-creation/container-creation/root-bin/js/package.json +++ b/container-creation/container-creation/root-bin/js/package.json @@ -1,7 +1,28 @@ { + "name": "container-creation-js", + "version": "1.0.0", + "description": "Authentication modules for container creation", + "main": "runner.js", + "scripts": { + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" + }, "dependencies": { - "axios": "^1.10.0", - "https": "^1.0.0", - "qs": "^6.14.0" + "axios": "^1.12.1", + "qs": "^6.12.0" + }, + "devDependencies": { + "jest": "^29.7.0", + "axios-mock-adapter": "^1.22.0" + }, + "jest": { + "testEnvironment": "node", + "coverageDirectory": "coverage", + "collectCoverageFrom": [ + "*.js", + "!runner.js", + "!coverage/**" + ] } } \ No newline at end of file From 41ac406f5f0135b307216349c19595cffa1973e7 Mon Sep 17 00:00:00 2001 From: Robert Gingras Date: Mon, 20 Oct 2025 14:17:43 -0400 Subject: [PATCH 7/7] add deploy instructions --- container-creation/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/container-creation/README.md b/container-creation/README.md index f1ebd384..290796ee 100644 --- a/container-creation/README.md +++ b/container-creation/README.md @@ -14,4 +14,8 @@ - [`register-agent.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/register-agent.sh) - Installs wazuh-agent from it's repos then registers it with the manager. - [`deployOnStart.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/deployOnStart.sh) - clones `PROJECT_REPOSITORY` to /root on the container, then copies each set of env vars from `gatherEnvVars.sh` to the appropriate component directories, then calls the appropriate [`node_runtime_install`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/node_runtime_install.sh) or [`python_runtime_install`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/python_runtime_install.sh) scripts for component runtimes, then runs `INSTALL_CMD` and executes the file created in `gatherServices.sh` - [`register-container.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/register-container.sh) - interogates for `container_ip`, `hostname`, `os_release`, and `mac`, then checks if an SSH port forwards has already been allocated via IPTables on intern-phxdc-pve1 and allocates one if not. Similarly checks then allocates additional port-forwards also via IPTables on intern-phxdc-pve1. Then `port_map.json`is updated via an ssh connection to the NGINX container using `jq` to perform the update. -- [`start_services.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh) - Stops `CONTAINER_ID`, sources [`create-template.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/create-template.sh), starts `CONTAINER_ID`, starts `github-runner.service` in the container if `GH_ACTION`, runs `BUILD_COMMAND`, then `START_COMMAND` for each component and `ROOT_START_COMMAND` when `MULTI_COMPONENT`. \ No newline at end of file +- [`start_services.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/start_services.sh) - Stops `CONTAINER_ID`, sources [`create-template.sh`](./intern-phxdc-pve1/var-lib-vz-snippets/helper-scripts/create-template.sh), starts `CONTAINER_ID`, starts `github-runner.service` in the container if `GH_ACTION`, runs `BUILD_COMMAND`, then `START_COMMAND` for each component and `ROOT_START_COMMAND` when `MULTI_COMPONENT`. + +## Deployment + +This folder is structured to indicate deployment. Essentially, on any changes, copy all changed files to the server indicated by the "intern-phxdc-pve1" and "container-creation" subfolders, then to the correct directory on those servers. I.e. "intern-phxdc-pve1/var-lib-vz-snippets/create-container.sh" would be RSYNCed or otherwise copied to "intern-pxdc-pve1:/var/lib/vz/snippets/create-container.sh". \ No newline at end of file