diff --git a/.ci/Arch/Dockerfile b/.ci/Arch/Dockerfile deleted file mode 100644 index 36cf5c4ae..000000000 --- a/.ci/Arch/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -from archlinux:latest - -RUN pacman --sync --refresh --sysupgrade --needed --noconfirm \ - base-devel \ - ccache \ - cmake \ - git \ - gtest \ - mariadb-libs \ - ninja \ - protobuf \ - qt6-base \ - qt6-imageformats \ - qt6-multimedia \ - qt6-svg \ - qt6-tools \ - qt6-translations \ - qt6-websockets \ - && pacman --sync --clean --clean --noconfirm diff --git a/.ci/Debian11/Dockerfile b/.ci/Debian11/Dockerfile deleted file mode 100644 index b994863bf..000000000 --- a/.ci/Debian11/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM debian:11 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt5multimedia5-plugins \ - libqt5sql5-mysql \ - libqt5svg5-dev \ - libqt5websockets5-dev \ - ninja-build \ - protobuf-compiler \ - qt5-image-formats-plugins \ - qtmultimedia5-dev \ - qttools5-dev \ - qttools5-dev-tools \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Debian12/Dockerfile b/.ci/Debian12/Dockerfile deleted file mode 100644 index 202405b84..000000000 --- a/.ci/Debian12/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM debian:12 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - libgl-dev \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt6multimedia6 \ - libqt6sql6-mysql \ - ninja-build \ - protobuf-compiler \ - qt6-image-formats-plugins \ - qt6-l10n-tools \ - qt6-multimedia-dev \ - qt6-svg-dev \ - qt6-tools-dev \ - qt6-tools-dev-tools \ - qt6-websockets-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Debian13/Dockerfile b/.ci/Debian13/Dockerfile deleted file mode 100644 index d7ab6ac86..000000000 --- a/.ci/Debian13/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM debian:13 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ca-certificates \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - libgl-dev \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt6multimedia6 \ - libqt6sql6-mysql \ - ninja-build \ - protobuf-compiler \ - qt6-image-formats-plugins \ - qt6-l10n-tools \ - qt6-multimedia-dev \ - qt6-svg-dev \ - qt6-tools-dev \ - qt6-tools-dev-tools \ - qt6-websockets-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Fedora42/Dockerfile b/.ci/Fedora42/Dockerfile deleted file mode 100644 index ee4a856f2..000000000 --- a/.ci/Fedora42/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM fedora:42 - -RUN dnf install -y \ - ccache \ - cmake \ - gcc-c++ \ - git \ - mariadb-devel \ - ninja-build \ - protobuf-devel \ - qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \ - qt6-qtimageformats \ - rpm-build \ - xz-devel \ - zlib-devel \ - && dnf clean all diff --git a/.ci/Fedora43/Dockerfile b/.ci/Fedora43/Dockerfile deleted file mode 100644 index 27570cf99..000000000 --- a/.ci/Fedora43/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM fedora:43 - -RUN dnf install -y \ - ccache \ - cmake \ - gcc-c++ \ - git \ - mariadb-devel \ - ninja-build \ - protobuf-devel \ - qt6-{qttools,qtsvg,qtmultimedia,qtwebsockets}-devel \ - qt6-qtimageformats \ - rpm-build \ - xz-devel \ - zlib-devel \ - && dnf clean all diff --git a/.ci/Servatrice_Debian11/Dockerfile b/.ci/Servatrice_Debian11/Dockerfile deleted file mode 100644 index fadc9e0e7..000000000 --- a/.ci/Servatrice_Debian11/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM debian:11 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt5sql5-mysql \ - libqt5websockets5-dev \ - ninja-build \ - protobuf-compiler \ - qttools5-dev \ - qttools5-dev-tools \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Ubuntu22.04/Dockerfile b/.ci/Ubuntu22.04/Dockerfile deleted file mode 100644 index 93c8fdea9..000000000 --- a/.ci/Ubuntu22.04/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM ubuntu:22.04 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt5multimedia5-plugins \ - libqt5sql5-mysql \ - libqt5svg5-dev \ - libqt5websockets5-dev \ - ninja-build \ - protobuf-compiler \ - qt5-image-formats-plugins \ - qtmultimedia5-dev \ - qttools5-dev \ - qttools5-dev-tools \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Ubuntu24.04/Dockerfile b/.ci/Ubuntu24.04/Dockerfile deleted file mode 100644 index 809b2e43a..000000000 --- a/.ci/Ubuntu24.04/Dockerfile +++ /dev/null @@ -1,28 +0,0 @@ -FROM ubuntu:24.04 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - libgl-dev \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt6multimedia6 \ - libqt6sql6-mysql \ - ninja-build \ - protobuf-compiler \ - qt6-image-formats-plugins \ - qt6-l10n-tools \ - qt6-multimedia-dev \ - qt6-svg-dev \ - qt6-tools-dev \ - qt6-tools-dev-tools \ - qt6-websockets-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/Ubuntu26.04/Dockerfile b/.ci/Ubuntu26.04/Dockerfile deleted file mode 100644 index 7b0cd389f..000000000 --- a/.ci/Ubuntu26.04/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM ubuntu:26.04 - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - ca-certificates \ - ccache \ - clang-format \ - cmake \ - file \ - g++ \ - git \ - libgl-dev \ - liblzma-dev \ - libmariadb-dev-compat \ - libprotobuf-dev \ - libqt6multimedia6 \ - libqt6sql6-mysql \ - ninja-build \ - protobuf-compiler \ - qt6-image-formats-plugins \ - qt6-l10n-tools \ - qt6-multimedia-dev \ - qt6-svg-dev \ - qt6-tools-dev \ - qt6-tools-dev-tools \ - qt6-websockets-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/.ci/compile.sh b/.ci/compile.sh deleted file mode 100755 index 7ebdd6e4e..000000000 --- a/.ci/compile.sh +++ /dev/null @@ -1,322 +0,0 @@ -#!/bin/bash - -# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else. - -# Compiles cockatrice inside of a ci environment -# --install runs make install -# --package [] runs make package, optionally force the type -# --suffix renames package with this suffix, requires arg -# --server compiles servatrice -# --test runs tests -# --debug or --release sets the build type ie CMAKE_BUILD_TYPE -# --ccache [] uses ccache and shows stats, optionally provide size -# --evict-ccache runs ccache eviction based on given age after build -# --dir sets the name of the build dir, default is "build" -# --cmake-generator sets CMAKE_GENERATOR as used by cmake -# --target-macos-version sets the min os version - only used for macOS builds -# uses env: BUILDTYPE MAKE_INSTALL MAKE_PACKAGE PACKAGE_TYPE PACKAGE_SUFFIX MAKE_SERVER MAKE_NO_CLIENT MAKE_TEST USE_CCACHE CCACHE_SIZE CCACHE_EVICTION_AGE BUILD_DIR CMAKE_GENERATOR TARGET_MACOS_VERSION -# (correspond to args: --debug/--release --install --package --suffix --server --test --ccache --dir ) -# exitcode: 1 for failure, 3 for invalid arguments - -# Read arguments -while [[ $# != 0 ]]; do - case "$1" in - '--') - shift - ;; - '--install') - MAKE_INSTALL=1 - shift - ;; - '--package') - MAKE_PACKAGE=1 - shift - if [[ $# != 0 && ${1:0:1} != - ]]; then - PACKAGE_TYPE="$1" - shift - fi - ;; - '--suffix') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--suffix expects an argument" - exit 3 - fi - PACKAGE_SUFFIX="$1" - shift - ;; - '--server') - MAKE_SERVER=1 - shift - ;; - '--no-client') - MAKE_NO_CLIENT=1 - shift - ;; - '--test') - MAKE_TEST=1 - shift - ;; - '--debug') - BUILDTYPE="Debug" - shift - ;; - '--release') - BUILDTYPE="Release" - shift - ;; - '--ccache') - USE_CCACHE=1 - shift - if [[ $# != 0 && ${1:0:1} != - ]]; then - CCACHE_SIZE="$1" - shift - fi - ;; - '--evict-ccache') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--evict-ccache expects an argument" - exit 3 - fi - CCACHE_EVICTION_AGE=$1 - shift - ;; - '--vcpkg') - USE_VCPKG=1 - shift - ;; - '--dir') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--dir expects an argument" - exit 3 - fi - BUILD_DIR="$1" - shift - ;; - '--cmake-generator') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--cmake-generator expects an argument" - exit 3 - fi - export CMAKE_GENERATOR=$1 - shift - ;; - '--target-macos-version') - shift - if [[ $# == 0 ]]; then - echo "::error file=$0::--target-macos-version expects an argument" - exit 3 - fi - TARGET_MACOS_VERSION="$1" - shift - ;; - *) - echo "::error file=$0::unrecognized option: $1" - exit 3 - ;; - esac -done - -set -e - -# Setup -./servatrice/check_schema_version.sh -if [[ ! $BUILDTYPE ]]; then - BUILDTYPE=Release -fi -if [[ ! $BUILD_DIR ]]; then - BUILD_DIR="build" -fi -mkdir -p "$BUILD_DIR" -cd "$BUILD_DIR" - -# Set minimum CMake Version -export CMAKE_POLICY_VERSION_MINIMUM=3.10 - -# Add cmake flags -flags=("-DCMAKE_BUILD_TYPE=$BUILDTYPE") -if [[ $MAKE_SERVER ]]; then - flags+=("-DWITH_SERVER=1") -fi -if [[ $MAKE_NO_CLIENT ]]; then - flags+=("-DWITH_CLIENT=0" "-DWITH_ORACLE=0") -fi -if [[ $MAKE_TEST ]]; then - flags+=("-DTEST=1") -fi -if [[ $USE_CCACHE ]]; then - flags+=("-DUSE_CCACHE=1") - if [[ $CCACHE_SIZE ]]; then - # note, this setting persists after running the script - ccache --max-size "$CCACHE_SIZE" - fi -fi -if [[ $PACKAGE_TYPE ]]; then - flags+=("-DCPACK_GENERATOR=$PACKAGE_TYPE") -fi -if [[ $USE_VCPKG ]]; then - flags+=("-DUSE_VCPKG=1") -fi - -# Add cmake --build flags -buildflags=(--config "$BUILDTYPE") - -function ccachestatsverbose() { - # note, verbose only works on newer ccache, discard the error - local got - if got="$(ccache --show-stats --verbose 2>/dev/null)"; then - echo "$got" - else - ccache --show-stats - fi -} - -# Compile -if [[ $RUNNER_OS == macOS ]]; then - # QTDIR is needed for macOS since we actually only use the cached thin Qt binaries instead of the install-qt-action, - # which sets a few environment variables - if QTDIR=$(find "$GITHUB_WORKSPACE/Qt" -depth -maxdepth 2 -name macos -type d -print -quit); then - echo "found QTDIR at $QTDIR" - else - echo "could not find QTDIR!" - exit 2 - fi - # the qtdir is located at Qt/[qtversion]/macos - # we use find to get the first subfolder with the name "macos" - # this works independent of the qt version as there should be only one version installed on the runner at a time - export QTDIR - - if [[ $TARGET_MACOS_VERSION ]]; then - # CMAKE_OSX_DEPLOYMENT_TARGET is a vanilla cmake flag needed to compile to target macOS version - flags+=("-DCMAKE_OSX_DEPLOYMENT_TARGET=$TARGET_MACOS_VERSION") - - # vcpkg dependencies need a vcpkg triplet file to compile to the target macOS version - # an easy way is to copy the x64-osx.cmake file and modify it - triplets_dir="/tmp/cmake/triplets" - triplet_version="custom-triplet" - triplet_file="$triplets_dir/$triplet_version.cmake" - arch=$(uname -m) - if [[ $arch == x86_64 ]]; then - arch="x64" - fi - mkdir -p "$triplets_dir" - cp "../vcpkg/triplets/$arch-osx.cmake" "$triplet_file" - echo "set(VCPKG_CMAKE_SYSTEM_VERSION $TARGET_MACOS_VERSION)" >>"$triplet_file" - echo "set(VCPKG_OSX_DEPLOYMENT_TARGET $TARGET_MACOS_VERSION)" >>"$triplet_file" - flags+=("-DVCPKG_OVERLAY_TRIPLETS=$triplets_dir") - flags+=("-DVCPKG_HOST_TRIPLET=$triplet_version") - flags+=("-DVCPKG_TARGET_TRIPLET=$triplet_version") - echo "::group::Generated triplet $triplet_file" - cat "$triplet_file" - echo "::endgroup::" - fi - - echo "::group::Signing Certificate" - if [[ -n "$MACOS_CERTIFICATE_NAME" ]]; then - echo "$MACOS_CERTIFICATE" | base64 --decode >"certificate.p12" - security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain - security default-keychain -s build.keychain - security set-keychain-settings -t 3600 -l build.keychain - security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain - security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign - security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain - echo "macOS signing certificate successfully imported and keychain configured." - else - echo "No signing certificate configured. Skipping set up of keychain in macOS environment." - fi - echo "::endgroup::" - - if [[ $MAKE_PACKAGE ]]; then - # Workaround https://github.com/actions/runner-images/issues/7522 - # have hdiutil repeat the command 10 times in hope of success - hdiutil_script="/tmp/hdiutil.sh" - # shellcheck disable=SC2016 - echo '#!/bin/bash - i=0 - while ! hdiutil "$@"; do - if (( ++i >= 10 )); then - echo "Error: hdiutil failed $i times!" >&2 - break - fi - sleep 1 - done' >"$hdiutil_script" - chmod +x "$hdiutil_script" - flags+=(-DCPACK_COMMAND_HDIUTIL="$hdiutil_script") - fi - -elif [[ $RUNNER_OS == Windows ]]; then - # Enable MTT, see https://devblogs.microsoft.com/cppblog/improved-parallelism-in-msbuild/ - # and https://devblogs.microsoft.com/cppblog/cpp-build-throughput-investigation-and-tune-up/#multitooltask-mtt - buildflags+=(-- -p:UseMultiToolTask=true -p:EnableClServerMode=true) -fi - -if [[ $USE_CCACHE ]]; then - echo "::group::Show ccache stats" - ccachestatsverbose - echo "::endgroup::" -fi - -echo "::group::Configure cmake" -cmake --version -echo "Running cmake with flags: ${flags[*]}" -cmake .. "${flags[@]}" -echo "::endgroup::" - -echo "::group::Build project" -echo "Running cmake --build with flags: ${buildflags[*]}" -cmake --build . "${buildflags[@]}" -echo "::endgroup::" - -if [[ $USE_CCACHE ]]; then - if [[ $CCACHE_EVICTION_AGE ]]; then - echo "::group::evict ccache files older than $CCACHE_EVICTION_AGE" - ccache --evict-older-than "$CCACHE_EVICTION_AGE" - echo "::endgroup::" - fi - echo "::group::Show ccache stats again" - ccachestatsverbose - echo "::endgroup::" -elif [[ $CCACHE_EVICTION_AGE ]]; then - echo "::error file=$0::ccache eviction is enabled while ccache is disabled!" -fi - -if [[ $RUNNER_OS == macOS ]]; then - echo "::group::Inspect Mach-O binaries" - for app in cockatrice oracle servatrice; do - binary="$GITHUB_WORKSPACE/build/$app/$app.app/Contents/MacOS/$app" - echo "Inspecting $app..." - vtool -show-build "$binary" - file "$binary" - lipo -info "$binary" - echo "" - done - echo "::endgroup::" -fi - -if [[ $MAKE_TEST ]]; then - echo "::group::Run tests" - ctest -C "$BUILDTYPE" --output-on-failure - echo "::endgroup::" -fi - -if [[ $MAKE_INSTALL ]]; then - echo "::group::Install" - cmake --build . --target install --config "$BUILDTYPE" - echo "::endgroup::" -fi - -if [[ $MAKE_PACKAGE ]]; then - echo "::group::Create package" - cmake --build . --target package --config "$BUILDTYPE" - echo "::endgroup::" - - if [[ $PACKAGE_SUFFIX ]]; then - echo "::group::Update package name" - cd .. - BUILD_DIR="$BUILD_DIR" .ci/name_build.sh "$PACKAGE_SUFFIX" - echo "::endgroup::" - fi -fi diff --git a/.ci/docker.sh b/.ci/docker.sh deleted file mode 100644 index 46112daaa..000000000 --- a/.ci/docker.sh +++ /dev/null @@ -1,188 +0,0 @@ -#!/bin/bash - -# This script is to be used by the ci environment from the project root directory, do not use it from somewhere else. - -# Creates or loads docker images to use in compilation, creates RUN function to start compilation on the docker image. -# -# usage: source - - - - - - - - - - diff --git a/doc/doxygen/html/header.html b/doc/doxygen/html/header.html deleted file mode 100644 index 261581add..000000000 --- a/doc/doxygen/html/header.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - $projectname: $title - $title - - - - - - - - - - - - - $treeview - $search - $mathjax - $darkmode - - $extrastylesheet - - - -
- - -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
$projectname $projectnumber - -
- -
$projectbrief
-
-
$projectbrief
-
$searchbox
$searchbox
-
- - diff --git a/doc/doxygen/images/classic_database_display.png b/doc/doxygen/images/classic_database_display.png deleted file mode 100644 index fb762d9a3..000000000 Binary files a/doc/doxygen/images/classic_database_display.png and /dev/null differ diff --git a/doc/doxygen/images/classic_database_display_add_buttons.png b/doc/doxygen/images/classic_database_display_add_buttons.png deleted file mode 100644 index b9cb7cd32..000000000 Binary files a/doc/doxygen/images/classic_database_display_add_buttons.png and /dev/null differ diff --git a/doc/doxygen/images/classic_deck_editor.png b/doc/doxygen/images/classic_deck_editor.png deleted file mode 100644 index 9371651e9..000000000 Binary files a/doc/doxygen/images/classic_deck_editor.png and /dev/null differ diff --git a/doc/doxygen/images/deck_dock_deck_list.png b/doc/doxygen/images/deck_dock_deck_list.png deleted file mode 100644 index bf12d97c2..000000000 Binary files a/doc/doxygen/images/deck_dock_deck_list.png and /dev/null differ diff --git a/doc/doxygen/images/deck_dock_deck_list_buttons.png b/doc/doxygen/images/deck_dock_deck_list_buttons.png deleted file mode 100644 index cb66b41da..000000000 Binary files a/doc/doxygen/images/deck_dock_deck_list_buttons.png and /dev/null differ diff --git a/doc/doxygen/images/deck_dock_deck_list_group_by.png b/doc/doxygen/images/deck_dock_deck_list_group_by.png deleted file mode 100644 index ba23b1fb5..000000000 Binary files a/doc/doxygen/images/deck_dock_deck_list_group_by.png and /dev/null differ diff --git a/doc/doxygen/images/deckeditordeckdockwidget.png b/doc/doxygen/images/deckeditordeckdockwidget.png deleted file mode 100644 index 33b9588d0..000000000 Binary files a/doc/doxygen/images/deckeditordeckdockwidget.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector.png b/doc/doxygen/images/printing_selector.png deleted file mode 100644 index c06ac90bc..000000000 Binary files a/doc/doxygen/images/printing_selector.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector_disable.png b/doc/doxygen/images/printing_selector_disable.png deleted file mode 100644 index 8bdd8f292..000000000 Binary files a/doc/doxygen/images/printing_selector_disable.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector_enable.png b/doc/doxygen/images/printing_selector_enable.png deleted file mode 100644 index 2c7d05afa..000000000 Binary files a/doc/doxygen/images/printing_selector_enable.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector_navigation.png b/doc/doxygen/images/printing_selector_navigation.png deleted file mode 100644 index a91ed4511..000000000 Binary files a/doc/doxygen/images/printing_selector_navigation.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector_options.png b/doc/doxygen/images/printing_selector_options.png deleted file mode 100644 index 2079b70ac..000000000 Binary files a/doc/doxygen/images/printing_selector_options.png and /dev/null differ diff --git a/doc/doxygen/images/printing_selector_pre_providerid.png b/doc/doxygen/images/printing_selector_pre_providerid.png deleted file mode 100644 index a1be993ee..000000000 Binary files a/doc/doxygen/images/printing_selector_pre_providerid.png and /dev/null differ diff --git a/doc/doxygen/images/release_channel_beta.png b/doc/doxygen/images/release_channel_beta.png deleted file mode 100644 index 00869a9cf..000000000 Binary files a/doc/doxygen/images/release_channel_beta.png and /dev/null differ diff --git a/doc/doxygen/images/vde_deck_analytics.png b/doc/doxygen/images/vde_deck_analytics.png deleted file mode 100644 index 0b4b11e19..000000000 Binary files a/doc/doxygen/images/vde_deck_analytics.png and /dev/null differ diff --git a/doc/doxygen/images/vde_flat_layout_color_grouped.png b/doc/doxygen/images/vde_flat_layout_color_grouped.png deleted file mode 100644 index 51f176668..000000000 Binary files a/doc/doxygen/images/vde_flat_layout_color_grouped.png and /dev/null differ diff --git a/doc/doxygen/images/vde_flat_layout_type_grouped.png b/doc/doxygen/images/vde_flat_layout_type_grouped.png deleted file mode 100644 index 4a33e0017..000000000 Binary files a/doc/doxygen/images/vde_flat_layout_type_grouped.png and /dev/null differ diff --git a/doc/doxygen/images/vde_overlap_layout_type_grouped.png b/doc/doxygen/images/vde_overlap_layout_type_grouped.png deleted file mode 100644 index 606144586..000000000 Binary files a/doc/doxygen/images/vde_overlap_layout_type_grouped.png and /dev/null differ diff --git a/doc/doxygen/images/vde_sample_hand.png b/doc/doxygen/images/vde_sample_hand.png deleted file mode 100644 index 95924a5d4..000000000 Binary files a/doc/doxygen/images/vde_sample_hand.png and /dev/null differ diff --git a/doc/doxygen/js/graph_toggle.js b/doc/doxygen/js/graph_toggle.js deleted file mode 100644 index 3d8fa83d0..000000000 --- a/doc/doxygen/js/graph_toggle.js +++ /dev/null @@ -1,147 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - document.querySelectorAll(".memdoc").forEach(memdoc => { - let callContent = null, callHeader = null; - let callerContent = null, callerHeader = null; - - memdoc.querySelectorAll("div.dynheader").forEach(header => { - const text = (header.textContent || "").trim().toLowerCase(); - let content = header.nextElementSibling; - let tries = 0; - while (content && !content.classList.contains("dyncontent") && tries < 8) { - content = content.nextElementSibling; - tries++; - } - if (!content) return; - - if (text.includes("caller")) { - callerContent = content; - callerHeader = header; - } else if (text.includes("call graph") || text.includes("callgraph") || text.includes("call graph for")) { - callContent = content; - callHeader = header; - } else if (text.includes("call")) { - if (!callContent) { - callContent = content; - callHeader = header; - } - } - }); - - if (!callContent && !callerContent) return; - if (memdoc.querySelector(".graph-toggle")) return; - - const toggle = document.createElement("div"); - toggle.className = "graph-toggle"; - - const callerBtn = document.createElement("button"); - callerBtn.type = "button"; - callerBtn.textContent = "Caller Graph"; - const callBtn = document.createElement("button"); - callBtn.type = "button"; - callBtn.textContent = "Call Graph"; - - toggle.appendChild(callerBtn); - toggle.appendChild(callBtn); - - const firstHeader = memdoc.querySelector("div.dynheader"); - memdoc.insertBefore(toggle, firstHeader || memdoc.firstChild); - - // hide everything initially - if (callerContent) { - callerContent.style.display = "none"; - callerHeader.style.display = "none"; - } - if (callContent) { - callContent.style.display = "none"; - callHeader.style.display = "none"; - } - - // disable missing buttons - if (!callerContent) { - callerBtn.disabled = true; - callerBtn.classList.add("disabled"); - } - if (!callContent) { - callBtn.disabled = true; - callBtn.classList.add("disabled"); - } - - // track current state - let current = null; // "caller", "call", "both", null=hidden - - function setActive(type) { - if (type === "caller") { - if (current === "caller") { // hide it - if (callerContent) { - callerContent.style.display = "none"; - callerHeader.style.display = "none"; - } - current = null; - } else if (current === "call") { // show both - if (callerContent) { - callerContent.style.display = "block"; - callerHeader.style.display = "block"; - } - current = "both"; - } else if (current === "both") { // hide caller only → call only - if (callerContent) { - callerContent.style.display = "none"; - callerHeader.style.display = "none"; - } - current = "call"; - } else { // nothing visible → show caller - if (callerContent) { - callerContent.style.display = "block"; - callerHeader.style.display = "block"; - } - if (callContent) { - callContent.style.display = "none"; - callHeader.style.display = "none"; - } - current = "caller"; - } - } else if (type === "call") { - if (current === "call") { // hide it - if (callContent) { - callContent.style.display = "none"; - callHeader.style.display = "none"; - } - current = null; - } else if (current === "caller") { // show both - if (callContent) { - callContent.style.display = "block"; - callHeader.style.display = "block"; - } - current = "both"; - } else if (current === "both") { // hide call only → caller only - if (callContent) { - callContent.style.display = "none"; - callHeader.style.display = "none"; - } - current = "caller"; - } else { // nothing visible → show call only - if (callContent) { - callContent.style.display = "block"; - callHeader.style.display = "block"; - } - if (callerContent) { - callerContent.style.display = "none"; - callerHeader.style.display = "none"; - } - current = "call"; - } - } - - // update button styles - callerBtn.classList.toggle("active", current === "caller" || current === "both"); - callBtn.classList.toggle("active", current === "call" || current === "both"); - } - - callerBtn.addEventListener("click", () => { - if (!callerBtn.disabled) setActive("caller"); - }); - callBtn.addEventListener("click", () => { - if (!callBtn.disabled) setActive("call"); - }); - }); -}); diff --git a/doc/doxygen/theme b/doc/doxygen/theme deleted file mode 160000 index d52eafe3e..000000000 --- a/doc/doxygen/theme +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d52eafe3e9303399fda15661f3d7bb8fe3d7eabc diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6cbac61f0..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: '3' - -services: - mysql: - image: mysql - command: --sql_mode= - environment: - - MYSQL_ROOT_PASSWORD=root-password - - MYSQL_DATABASE=servatrice - - MYSQL_USER=servatrice - - MYSQL_PASSWORD=password - volumes: - - $PWD/servatrice/servatrice.sql:/docker-entrypoint-initdb.d/servatrice.sql - - servatrice: - build: - context: . - dockerfile: Dockerfile - image: ghcr.io/cockatrice/servatrice:latest - depends_on: - - mysql - ports: - - "4748:4748" - entrypoint: "/bin/bash -c 'sleep 10; servatrice --config /tmp/servatrice.ini --log-to-console'" - restart: always - volumes: - - $PWD/servatrice/docker/servatrice-docker.ini:/tmp/servatrice.ini diff --git a/docker-compose.yml.windows b/docker-compose.yml.windows deleted file mode 100644 index 3d29b1e5f..000000000 --- a/docker-compose.yml.windows +++ /dev/null @@ -1,27 +0,0 @@ -version: '3' - -services: - mysql: - image: mysql - environment: - - MYSQL_ROOT_PASSWORD=root-password - - MYSQL_DATABASE=servatrice - - MYSQL_USER=servatrice - - MYSQL_PASSWORD=password - volumes: - - ./servatrice/servatrice.sql:/docker-entrypoint-initdb.d/servatrice.sql - - ./servatrice/mysql-storage:/var/lib/mysql - - servatrice: - build: - context: . - dockerfile: Dockerfile - image: ghcr.io/cockatrice/servatrice:latest - depends_on: - - mysql - ports: - - "4748:4748" - entrypoint: "/bin/bash -c 'sleep 10; servatrice --config /tmp/servatrice.ini --log-to-console'" - restart: always - volumes: - - ./servatrice/docker/servatrice-docker.ini:/tmp/servatrice.ini diff --git a/format.sh b/format.sh deleted file mode 100755 index f8c183dfc..000000000 --- a/format.sh +++ /dev/null @@ -1,394 +0,0 @@ -#!/bin/bash - -# This script will run clang-format on all modified, non-3rd-party C++/Header files. -# Optionally runs cmake-format on all modified cmake files. -# Optionally runs shellcheck on all modified shell files. -# Uses clang-format cmake-format git diff find shellcheck -# Never, ever, should this receive a path with a newline in it. Don't bother proofing it for that. - -set -o pipefail - -# go to the project root directory, this file should be located in the project root directory -olddir="$PWD" -cd "${BASH_SOURCE%/*}/" || exit 2 # could not find path, this could happen with special links etc. - -# defaults -include=("cockatrice/src" \ -"libcockatrice_card" \ -"libcockatrice_deck_list" \ -"libcockatrice_network" \ -"libcockatrice_protocol" \ -"libcockatrice_rng" \ -"libcockatrice_settings" \ -"libcockatrice_utility" \ -"oracle/src" \ -"servatrice/src" \ -"tests") -exclude=("libcockatrice_rng/libcockatrice/rng/sfmt/" \ -"libcockatrice_utility/libcockatrice/utility/peglib.h" \ -"oracle/src/lzma/" \ -"oracle/src/qt-json/" \ -"oracle/src/zip/" \ -"servatrice/src/smtp/") -exts=("cpp" "h" "proto") -cf_cmd="clang-format" -branch="origin/master" -cmakefile="CMakeLists.txt" -cmakedir="cmake/.*\\.cmake" -cmakeinclude=("cmake/gtest-CMakeLists.txt.in") -scripts="*.sh" -color="--" -verbosity=0 -sep="----------" - -# parse options -while [[ $* ]]; do - case "$1" in - '-b'|'--branch') - branch=$2 - set_branch=1 - shift 2 - ;; - '--cmake') - do_cmake=1 - shift - ;; - '-c'|'--color-diff') - color="--color=always" - mode="diff" - shift - ;; - '-d'|'--diff') - mode="diff" - shift - ;; - '-h'|'--help') - cat <s are given, all source files in those directories of the project root -path are formatted. To only format changed files in these directories use the ---branch option in combination. has to be a path relative to the project -root path or a full path inside $PWD. - -USAGE: $0 [option] [--branch ] [ ...] - -DEFAULTS: -Current includes are: - ${include[@]/%/ - } -Default excludes are: - ${exclude[@]/%/ - } -OPTIONS: - -b, --branch - Compare to this git branch and format only files that differ. - If unspecified it defaults to origin/master. - To not compare to a branch this has to be explicitly set to "". - When not comparing to a branch, git will not be used at all and every - source file in the entire project will be parsed. - - --cmake - Use cmake-format to format cmake files as well. - - -c, --color-diff - Display a colored diff. Implies --diff. - Only available on systems which support 'diff --color'. - - -d, --diff - Display a diff. Implies --test. - - -h, --help - Display this message and exit. - - -n, --names - Display a list of filenames that require formatting. Implies --test. - - --no-clang-format - Do not check any source files for clang-format. - - --print-version - Print the version of clang-format being used before continuing. - - --shell - Use shellcheck to lint shell files. Not available in the default inline - mode. - - -t, --test - Do not edit files in place. Set exit code to 1 if changes are required. - - -v, --verbose - Display output on successes. - -EXIT CODES: - 0 on a successful format or if no files require formatting. - 1 if a file requires formatting. - 2 if given incorrect arguments. - 3 if clang-format could not be found. - -EXAMPLES: - $0 --branch $USER/patch-2 ${include[0]} - Formats all changed files compared to the git branch "$USER/patch-2" - in the directory ${include[0]}. - - $0 --test . || echo "code requires formatting" - Tests if the source files in the current directory are correctly - formatted and prints an error message if formatting is required. - - $0 --cmake --branch "" --no-clang-format - Unconditionally format all cmake files and no source files. -EOM - exit 0 - ;; - '-n'|'--names') - mode="name" - shift - ;; - '--no-clang-format') - include=() # do not check any dirs - shift - ;; - '--print-version') - print_version=1 - shift - ;; - '--shell') - do_shell=1 - shift - ;; - '-t'|'--test') - mode="code" - shift - ;; - '-v'|'--verbose') - verbosity=1 - shift - ;; - '--') - dashdash=1 - shift - ;; - *) - if [[ ! $dashdash && $1 =~ ^-- ]]; then - echo "error in parsing arguments of $0: $1 is an unrecognized option" >&2 - exit 2 # input error - fi - if [[ ! $1 ]] || next_dir=$(cd "$olddir" && cd -- "$1" && pwd); then - if ! [[ $set_include ]]; then - include=() # remove default includes - set_include=1 - fi - if [[ $1 ]]; then - if [[ $next_dir != $PWD/* ]]; then - echo "error in parsing arguments of $0: $next_dir is not in $PWD" >&2 - exit 2 # input error - fi - include+=("$next_dir") - fi - else - echo "error in parsing arguments of $0: $1 is not a directory" >&2 - exit 2 # input error - fi - if ! [[ $set_branch ]]; then - unset branch # unset branch if not set explicitly - fi - shift - ;; - esac -done - -# check availability of clang-format -if ! hash $cf_cmd 2>/dev/null; then - echo "could not find $cf_cmd" >&2 - # find any clang-format-x.x in /usr/bin - cf_cmd=$(find /usr/bin -regex '.*/clang-format-[0-9]+\.[0-9]+' -print -quit) - if [[ $cf_cmd ]]; then - echo "found $cf_cmd instead" >&2 - else - exit 3 # special exit code for missing dependency - fi -fi - -# check availability of cmake-format -if [[ $do_cmake ]] && ! hash cmake-format 2>/dev/null; then - echo "could not find cmake-format" >&2 - exit 3 -fi - -# check availability of shellcheck -if [[ $do_shell ]] && ! hash shellcheck 2>/dev/null; then - echo "could not find shellcheck" >&2 - exit 3 -fi - -if [[ $branch ]]; then - # get all dirty files through git - if ! base=$(git merge-base "$branch" HEAD); then - echo "could not find git merge base" >&2 - exit 2 # input error - fi - mapfile -t basenames < <(git diff --diff-filter=d --name-only "$base") - names=() - for ex in "${exts[@]}"; do - for path in "${include[@]}"; do - for name in "${basenames[@]}"; do - rx="^$path/.*\\.$ex$" - if [[ $name =~ $rx ]]; then - names+=("$name") - fi - done - done - done - if [[ $do_cmake ]]; then - cmake_names=() - for name in "${basenames[@]}"; do - dirrx="^$cmakedir$" - filerx="(^|/)$cmakefile$" - if [[ $name =~ $dirrx || $name =~ $filerx ]]; then - cmake_names+=("$name") - fi - for include in "${cmakeinclude[@]}"; do - if [[ $name == "$include" ]]; then - cmake_names+=("$name") - fi - done - done - fi - if [[ $do_shell ]]; then - shell_names=() - for name in "${basenames[@]}"; do - filerx="(^|/)$scripts$" - if [[ $name =~ $filerx ]]; then - shell_names+=("$name") - fi - done - fi -else - exts_o=() - for ext in "${exts[@]}"; do - exts_o+=(-o -name "*\\.$ext") - done - unset "exts_o[0]" # remove first -o - mapfile -t names < <(find "${include[@]}" -type f "${exts_o[@]}") - if [[ $do_cmake ]]; then - mapfile -t cmake_names < <(find . -maxdepth 2 -type f -name "$cmakefile" -o -path "./${cmakedir/.}") - cmake_names+=("${cmakeinclude[@]}") - fi - if [[ $do_shell ]]; then - mapfile -t shell_names < <(find . -maxdepth 5 -type f -name "$scripts") - fi -fi - -# filter excludes -for path in "${exclude[@]}"; do - for i in "${!names[@]}"; do - rx="^$path" - if [[ ${names[$i]} =~ $rx ]]; then - unset "names[$i]" - fi - done -done - -# optionally print version -if [[ $print_version ]]; then - $cf_cmd -version - [[ $do_cmake ]] && echo "cmake-format version $(cmake-format --version)" - [[ $do_shell ]] && echo "shellcheck $(shellcheck --version | grep "version:")" - echo "$sep" -fi - -if [[ ! ${cmake_names[*]} ]]; then - unset do_cmake -fi -if [[ ! ${shell_names[*]} ]]; then - unset do_shell -fi -if [[ ! ( ${names[*]} || $do_cmake || $do_shell ) ]]; then - exit 0 # nothing to format means format is successful! -fi - -# format -case $mode in - diff) - declare -i code=0 - files_to_format=() - for name in "${names[@]}"; do - if ! $cf_cmd "$name" | diff "$name" - -p "$color"; then - code=1 - files_to_format+=("$name") - fi - done - for name in "${cmake_names[@]}"; do - if ! cmake-format "$name" | diff "$name" - -p "$color"; then - code=1 - files_to_format+=("$name") - fi - done - for name in "${shell_names[@]}"; do - if ! shellcheck "$name"; then - code=1 - files_to_format+=("$name") - fi - done - if (( code>0 )); then - echo "$sep" - for name in "${files_to_format[@]}"; do - echo "$name" - done - fi - exit $code - ;; - name) - declare -i code=0 - for name in "${names[@]}"; do - if ! $cf_cmd "$name" | diff "$name" - -q >/dev/null; then - echo "$name" - code=1 - fi - done - for name in "${cmake_names[@]}"; do - if ! cmake-format "$name" --check; then - echo "$name" - code=1 - fi - done - for name in "${shell_names[@]}"; do - if ! shellcheck "$name" >/dev/null; then - echo "$name" - code=1 - fi - done - exit $code - ;; - code) - for name in "${names[@]}"; do - $cf_cmd "$name" | diff "$name" - -q >/dev/null || exit 1 - done - for name in "${cmake_names[@]}"; do - cmake-format "$name" --check || exit 1 - done - for name in "${shell_names[@]}"; do - shellcheck "$name" >/dev/null || exit 1 - done - ;; - *) - if [[ "${names[*]}" ]]; then - $cf_cmd -i "${names[@]}" - fi - if [[ $do_cmake ]]; then - cmake-format -i "${cmake_names[@]}" - fi - if [[ $do_shell ]]; then - echo "warning: --shell is not compatible with the current mode but shell files were modified!" >&2 - echo "recommendation: try $0 --diff --shell" >&2 - fi - if (( verbosity>0 )); then - count="${#names[*]}" - if [[ $do_cmake ]]; then - (( count+=${#cmake_names[*]} )) - fi - echo "parsed $count files that differ from base $branch" - fi - ;; -esac diff --git a/libcockatrice_card/CMakeLists.txt b/libcockatrice_card/CMakeLists.txt deleted file mode 100644 index dd3799e33..000000000 --- a/libcockatrice_card/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - libcockatrice/card/card_info.h - libcockatrice/card/card_info_comparator.h - libcockatrice/card/database/card_database.h - libcockatrice/card/database/card_database_loader.h - libcockatrice/card/database/card_database_manager.h - libcockatrice/card/database/card_database_querier.h - libcockatrice/card/database/parser/card_database_parser.h - libcockatrice/card/database/parser/cockatrice_xml_3.h - libcockatrice/card/database/parser/cockatrice_xml_4.h - libcockatrice/card/import/card_name_normalizer.h - libcockatrice/card/printing/exact_card.h - libcockatrice/card/printing/printing_info.h - libcockatrice/card/set/card_set.h - libcockatrice/card/relation/card_relation.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_card STATIC - ${MOC_SOURCES} - libcockatrice/card/card_info.cpp - libcockatrice/card/card_info_comparator.cpp - libcockatrice/card/database/card_database.cpp - libcockatrice/card/database/card_database_loader.cpp - libcockatrice/card/database/card_database_manager.cpp - libcockatrice/card/database/card_database_querier.cpp - libcockatrice/card/database/parser/card_database_parser.cpp - libcockatrice/card/database/parser/cockatrice_xml_3.cpp - libcockatrice/card/database/parser/cockatrice_xml_4.cpp - libcockatrice/card/import/card_name_normalizer.cpp - libcockatrice/card/printing/exact_card.cpp - libcockatrice/card/printing/printing_info.cpp - libcockatrice/card/relation/card_relation.cpp - libcockatrice/card/set/card_set.cpp - libcockatrice/card/set/card_set_list.cpp - libcockatrice/card/format/format_legality_rules.cpp - libcockatrice/card/format/format_legality_rules.h -) - -target_include_directories( - libcockatrice_card - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_SOURCE_DIR}/cockatrice/src/filters -) - -target_link_libraries( - libcockatrice_card - PUBLIC libcockatrice_interfaces - PUBLIC libcockatrice_utility - PUBLIC ${QT_CORE_MODULE} -) diff --git a/libcockatrice_card/libcockatrice/card/card_info.cpp b/libcockatrice_card/libcockatrice/card/card_info.cpp deleted file mode 100644 index 1aaec85b8..000000000 --- a/libcockatrice_card/libcockatrice/card/card_info.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include "card_info.h" - -#include "game_specific_terms.h" -#include "printing/printing_info.h" -#include "relation/card_relation.h" -#include "set/card_set.h" - -#include -#include -#include -#include -#include -#include -#include - -class CardRelation; -class CardSet; -class CardInfo; - -using CardInfoPtr = QSharedPointer; - -CardInfo::CardInfo(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - SetToPrintingsMap _sets, - const UiAttributes _uiAttributes) - : name(_name), text(_text), isToken(_isToken), properties(std::move(_properties)), relatedCards(_relatedCards), - reverseRelatedCards(_reverseRelatedCards), setsToPrintings(std::move(_sets)), uiAttributes(_uiAttributes) -{ - simpleName = CardInfo::simplifyName(name); - - refreshCachedSets(); -} - -CardInfoPtr CardInfo::newInstance(const QString &_name) -{ - return newInstance(_name, "", false, {}, {}, {}, {}, {}); -} - -CardInfoPtr CardInfo::newInstance(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - SetToPrintingsMap _sets, - const UiAttributes _uiAttributes) -{ - CardInfoPtr ptr(new CardInfo(_name, _text, _isToken, std::move(_properties), _relatedCards, _reverseRelatedCards, - _sets, _uiAttributes)); - ptr->setSmartPointer(ptr); - - for (const auto &printings : _sets) { - for (const PrintingInfo &printing : printings) { - printing.getSet()->append(ptr); - break; - } - } - - return ptr; -} - -QString CardInfo::getCorrectedName() const -{ - // remove all the characters reserved in windows file paths, - // other oses only disallow a subset of these so it covers all - static const QRegularExpression rmrx(R"(( // |[*<>:"\\?\x00-\x08\x10-\x1f]))"); - static const QRegularExpression spacerx(R"([/\x09-\x0f])"); - static const QString space(' '); - QString result = name; - // Fire // Ice, Circle of Protection: Red, "Ach! Hans, Run!", Who/What/When/Where/Why, Question Elemental? - return result.remove(rmrx).replace(spacerx, space); -} - -QString CardInfo::getLegalityProp(const QString &format) const -{ - return getProperty("format-" + format); -} - -bool CardInfo::isLegalInFormat(const QString &format) const -{ - if (format.isEmpty()) { - return true; - } - - QString formatLegality = getLegalityProp(format); - return formatLegality == "legal" || formatLegality == "restricted"; -} - -void CardInfo::addToSet(const CardSetPtr &_set, const PrintingInfo &_info) -{ - if (!_set->contains(smartThis)) { - _set->append(smartThis); - } - if (!setsToPrintings[_set->getShortName()].contains(_info)) { - setsToPrintings[_set->getShortName()].append(_info); - } - - refreshCachedSets(); -} - -void CardInfo::combineLegalities(const QVariantHash &props) -{ - QHashIterator it(props); - while (it.hasNext()) { - it.next(); - if (it.key().startsWith("format-")) { - smartThis->setProperty(it.key(), it.value().toString()); - } - } -} - -void CardInfo::refreshCachedSets() -{ - refreshCachedSetNames(); - refreshCachedAltNames(); -} - -void CardInfo::refreshCachedSetNames() -{ - QStringList setList; - // update the cached list of set names - for (const auto &printings : setsToPrintings) { - for (const auto &printing : printings) { - if (printing.getSet()->getEnabled()) { - setList << printing.getSet()->getShortName(); - } - break; - } - } - setsNames = setList.join(", "); -} - -void CardInfo::refreshCachedAltNames() -{ - altNames.clear(); - - // update the altNames with the flavorNames - for (const auto &printings : setsToPrintings) { - for (const auto &printing : printings) { - QString flavorName = printing.getFlavorName(); - if (!flavorName.isEmpty()) { - altNames.insert(flavorName); - } - } - } -} - -QString CardInfo::simplifyName(const QString &name) -{ - static const QRegularExpression spaceOrSplit("(\\s+|\\/\\/.*)"); - static const QRegularExpression nonAlnum("[^a-z0-9]"); - - QString simpleName = name.toLower(); - - // remove spaces and right halves of split cards - simpleName.remove(spaceOrSplit); - - // So Aetherling would work, but not Ætherling since 'Æ' would get replaced - // with nothing. - simpleName.replace("æ", "ae"); - - // Replace Jötun Grunt with Jotun Grunt. - simpleName = simpleName.normalized(QString::NormalizationForm_KD); - - // remove all non alphanumeric characters from the name - simpleName.remove(nonAlnum); - return simpleName; -} - -QChar CardInfo::getColorChar() const -{ - QString colors = getColors(); - switch (colors.size()) { - case 0: - return QChar(); - case 1: - return colors.at(0); - default: - return QChar('m'); - } -} - -void CardInfo::resetReverseRelatedCards2Me() -{ - for (CardRelation *cardRelation : this->getReverseRelatedCards2Me()) { - cardRelation->deleteLater(); - } - reverseRelatedCardsToMe = QList(); -} - -// Back-compatibility methods. Remove ASAP -QString CardInfo::getCardType() const -{ - return getProperty(Mtg::CardType); -} -void CardInfo::setCardType(const QString &value) -{ - setProperty(Mtg::CardType, value); -} -QString CardInfo::getCmc() const -{ - return getProperty(Mtg::ConvertedManaCost); -} -QString CardInfo::getColors() const -{ - return getProperty(Mtg::Colors); -} -void CardInfo::setColors(const QString &value) -{ - setProperty(Mtg::Colors, value); -} -QString CardInfo::getLoyalty() const -{ - return getProperty(Mtg::Loyalty); -} -QString CardInfo::getMainCardType() const -{ - return getProperty(Mtg::MainCardType); -} -QString CardInfo::getManaCost() const -{ - return getProperty(Mtg::ManaCost); -} -QString CardInfo::getPowTough() const -{ - return getProperty(Mtg::PowTough); -} -void CardInfo::setPowTough(const QString &value) -{ - setProperty(Mtg::PowTough, value); -} diff --git a/libcockatrice_card/libcockatrice/card/card_info.h b/libcockatrice_card/libcockatrice/card/card_info.h deleted file mode 100644 index 654ac1f63..000000000 --- a/libcockatrice_card/libcockatrice/card/card_info.h +++ /dev/null @@ -1,377 +0,0 @@ -#ifndef CARD_INFO_H -#define CARD_INFO_H - -#include "format/format_legality_rules.h" -#include "printing/printing_info.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(CardInfoLog, "card_info"); - -class CardInfo; -class CardSet; -class CardRelation; -class ICardDatabaseParser; - -typedef QSharedPointer CardInfoPtr; -typedef QSharedPointer CardSetPtr; -typedef QSharedPointer FormatRulesPtr; -typedef QMap> SetToPrintingsMap; - -typedef QHash CardNameMap; -typedef QHash SetNameMap; -typedef QHash FormatRulesNameMap; - -Q_DECLARE_METATYPE(CardInfoPtr) - -/** - * @class CardInfo - * @ingroup Cards - * - * @brief Represents a card and its associated metadata, properties, and relationships. - * - * CardInfo holds both static information (name, text, flags) and dynamic data - * (properties, set memberships, relationships). It also integrates with - * signals/slots, allowing observers to react to property or visual updates. - * - * Each CardInfo may belong to multiple sets through its printings, and can - * be related to other cards through defined relationships. - */ -class CardInfo : public QObject -{ - Q_OBJECT - -public: - /** - * @class CardInfo::UiAttributes - * @ingroup Cards - * - * @brief Attributes of the card that affect display and game logic. - */ - struct UiAttributes - { - bool cipt = false; ///< Positioning flag used by UI. - bool landscapeOrientation = false; ///< Orientation flag for rendering. - int tableRow = 0; ///< Row index in a table or visual representation. - bool upsideDownArt = false; ///< Whether artwork is flipped for visual purposes. - }; - -private: - /** @name Private Card Properties - * @anchor PrivateCardProperties - */ - ///@{ - CardInfoPtr smartThis; ///< Smart pointer to self for safe cross-references. - QString name; ///< Full name of the card. - QString simpleName; ///< Simplified name for fuzzy matching. - QString text; ///< Text description or rules text of the card. - bool isToken; ///< Whether this card is a token or not. - QVariantHash properties; ///< Key-value store of dynamic card properties. - QList relatedCards; ///< Forward references to related cards. - QList reverseRelatedCards; ///< Cards that refer back to this card. - QList reverseRelatedCardsToMe; ///< Cards that consider this card as related. - SetToPrintingsMap setsToPrintings; ///< Mapping from set names to printing variations. - UiAttributes uiAttributes; ///< Attributes that affect display and game logic - QString setsNames; ///< Cached, human-readable list of set names. - QSet altNames; ///< Cached set of alternate names, used when searching - ///@} - -public: - /** - * @brief Constructs a CardInfo with full initialization. - * - * @param _name The name of the card. - * @param _text Rules text or description of the card. - * @param _isToken Flag indicating whether the card is a token. - * @param _properties Arbitrary key-value properties. - * @param _relatedCards Forward references to related cards. - * @param _reverseRelatedCards Backward references to related cards. - * @param _sets Map of set names to printing information. - * @param _uiAttributes Attributes that affect display and game logic - */ - explicit CardInfo(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - SetToPrintingsMap _sets, - UiAttributes _uiAttributes); - - /** - * @brief Copy constructor for CardInfo. - * - * Performs a deep copy of properties, sets, and related card lists. - * - * @param other Another CardInfo to copy. - */ - CardInfo(const CardInfo &other) - : QObject(other.parent()), name(other.name), simpleName(other.simpleName), text(other.text), - isToken(other.isToken), properties(other.properties), relatedCards(other.relatedCards), - reverseRelatedCards(other.reverseRelatedCards), reverseRelatedCardsToMe(other.reverseRelatedCardsToMe), - setsToPrintings(other.setsToPrintings), uiAttributes(other.uiAttributes), setsNames(other.setsNames), - altNames(other.altNames) - { - } - - /** - * @brief Creates a new instance with only the card name. - * - * All other fields are set to defaults. - * - * @param _name The card name. - * @return Shared pointer to the new CardInfo instance. - */ - static CardInfoPtr newInstance(const QString &_name); - - /** - * @brief Creates a new instance with full initialization. - * - * @param _name Name of the card. - * @param _text Rules text or description. - * @param _isToken Token flag. - * @param _properties Arbitrary properties. - * @param _relatedCards Forward relationships. - * @param _reverseRelatedCards Reverse relationships. - * @param _sets Printing information per set. - * @param _uiAttributes Attributes that affect display and game logic - * @return Shared pointer to the new CardInfo instance. - */ - static CardInfoPtr newInstance(const QString &_name, - const QString &_text, - bool _isToken, - QVariantHash _properties, - const QList &_relatedCards, - const QList &_reverseRelatedCards, - SetToPrintingsMap _sets, - UiAttributes _uiAttributes); - - /** - * @brief Clones the current CardInfo instance. - * - * Uses the copy constructor and ensures the smart pointer is properly set. - * - * @return Shared pointer to the cloned CardInfo. - */ - [[nodiscard]] CardInfoPtr clone() const - { - auto newCardInfo = CardInfoPtr(new CardInfo(*this)); - newCardInfo->setSmartPointer(newCardInfo); // Set the smart pointer for the new instance - return newCardInfo; - } - - /** - * @brief Sets the internal smart pointer to self. - * - * Used internally to allow safe cross-references among CardInfo and CardSet. - * - * @param _ptr Shared pointer pointing to this instance. - */ - void setSmartPointer(CardInfoPtr _ptr) - { - smartThis = std::move(_ptr); - } - - /** @name Basic Properties Accessors */ //@{ - [[nodiscard]] inline const QString &getName() const - { - return name; - } - [[nodiscard]] const QString &getSimpleName() const - { - return simpleName; - } - const QSet &getAltNames() - { - return altNames; - } - [[nodiscard]] const QString &getText() const - { - return text; - } - void setText(const QString &_text) - { - text = _text; - emit cardInfoChanged(smartThis); - } - [[nodiscard]] bool getIsToken() const - { - return isToken; - } - [[nodiscard]] QStringList getProperties() const - { - return properties.keys(); - } - [[nodiscard]] QString getProperty(const QString &propertyName) const - { - return properties.value(propertyName).toString(); - } - void setProperty(const QString &_name, const QString &_value) - { - properties.insert(_name, _value); - emit cardInfoChanged(smartThis); - } - [[nodiscard]] bool hasProperty(const QString &propertyName) const - { - return properties.contains(propertyName); - } - [[nodiscard]] const SetToPrintingsMap &getSets() const - { - return setsToPrintings; - } - [[nodiscard]] const QString &getSetsNames() const - { - return setsNames; - } - //@} - - /** @name Related Cards Accessors */ //@{ - [[nodiscard]] const QList &getRelatedCards() const - { - return relatedCards; - } - [[nodiscard]] const QList &getReverseRelatedCards() const - { - return reverseRelatedCards; - } - [[nodiscard]] const QList &getReverseRelatedCards2Me() const - { - return reverseRelatedCardsToMe; - } - [[nodiscard]] QList getAllRelatedCards() const - { - QList result; - result.append(getRelatedCards()); - result.append(getReverseRelatedCards2Me()); - return result; - } - void resetReverseRelatedCards2Me(); - void addReverseRelatedCards2Me(CardRelation *cardRelation) - { - reverseRelatedCardsToMe.append(cardRelation); - } - //@} - - /** @name UI Positioning */ //@{ - [[nodiscard]] const UiAttributes &getUiAttributes() const - { - return uiAttributes; - } - //@} - - [[nodiscard]] QChar getColorChar() const; - - /** @name Legacy/Convenience Property Accessors */ //@{ - [[nodiscard]] QString getCardType() const; - void setCardType(const QString &value); - [[nodiscard]] QString getCmc() const; - [[nodiscard]] QString getColors() const; - void setColors(const QString &value); - [[nodiscard]] QString getLoyalty() const; - [[nodiscard]] QString getMainCardType() const; - [[nodiscard]] QString getManaCost() const; - [[nodiscard]] QString getPowTough() const; - void setPowTough(const QString &value); - //@} - - /** - * @brief Returns a version of the card name safe for file storage or fuzzy matching. - * - * Removes invalid characters, replaces spacing markers, and normalizes diacritics. - * - * @return Corrected card name as a QString. - */ - [[nodiscard]] QString getCorrectedName() const; - - /** - * @brief Gets the card's legality value for the given format. - * The legality prop for a format is stored in the property map under the key "format-" - * @param format The format's name. - * @return The card's legality value for the format. Empty if not found. - */ - [[nodiscard]] QString getLegalityProp(const QString &format) const; - - /** - * @brief Checks if the card is legal in the given format. - * A card is considered legal in a format if its properties map contains an entry for "format-", with value - * "legal" or "restricted". - * @param format The format's name. If empty, will always return true. - * @return Whether the card is legal in the given format. - */ - [[nodiscard]] bool isLegalInFormat(const QString &format) const; - - /** - * @brief Adds a printing to a specific set. - * - * Updates the mapping and refreshes the cached list of set names. - * - * @param _set The set to which the card should be added. - * @param _info Optional printing information. - */ - void addToSet(const CardSetPtr &_set, const PrintingInfo &_info = PrintingInfo()); - - /** - * @brief Combines legality properties from a provided map. - * - * Useful for merging format legality flags from multiple sources. - * - * @param props Key-value mapping of format legalities. - */ - void combineLegalities(const QVariantHash &props); - - /** - * @brief Refreshes all cached fields that are calculated from the contained sets and printings. - * - * Typically called after adding or modifying set memberships or printings. - */ - void refreshCachedSets(); - - /** - * @brief Simplifies a name for fuzzy matching. - * - * Converts to lowercase, removes punctuation/spacing. - * - * @param name Original name string. - * @return Simplified name string. - */ - static QString simplifyName(const QString &name); - -private: - /** - * @brief Refreshes the cached, human-readable list of set names. - * - * Typically called after adding or modifying set memberships. - */ - void refreshCachedSetNames(); - - /** - * @brief Refreshes the cached list of alt names for the card. - * - * Typically called after adding or modifying the contained printings. - */ - void refreshCachedAltNames(); - -signals: - /** - * @brief Emitted when a pixmap for this card has been updated or finished loading. - * - * @param printing Specific printing for which the pixmap has updated. - */ - void pixmapUpdated(const PrintingInfo &printing); - - /** - * @brief Emitted when card properties or state have changed. - * - * @param card Shared pointer to the CardInfo instance that changed. - */ - void cardInfoChanged(CardInfoPtr card); -}; -#endif diff --git a/libcockatrice_card/libcockatrice/card/card_info_comparator.cpp b/libcockatrice_card/libcockatrice/card/card_info_comparator.cpp deleted file mode 100644 index 821cc8675..000000000 --- a/libcockatrice_card/libcockatrice/card/card_info_comparator.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "card_info_comparator.h" - -CardInfoComparator::CardInfoComparator(const QStringList &properties, Qt::SortOrder order) - : m_properties(properties), m_order(order) -{ -} - -bool CardInfoComparator::operator()(const CardInfoPtr &a, const CardInfoPtr &b) const -{ - // Iterate over each property in the list - for (const QString &property : m_properties) { - QVariant valueA = getProperty(a, property); - QVariant valueB = getProperty(b, property); - - // Compare the current property - if (valueA != valueB) { - // If values differ, perform comparison - return compareVariants(valueA, valueB) ? (m_order == Qt::AscendingOrder) : (m_order == Qt::DescendingOrder); - } - } - - // If all properties are equal, return false (indicating they are considered equal for sorting purposes) - return false; -} - -bool CardInfoComparator::compareVariants(const QVariant &a, const QVariant &b) const -{ - // Determine the type of QVariant based on Qt version -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - if (a.typeId() != b.typeId()) { -#else - if (a.type() != b.type()) { -#endif - // If they are not the same type, compare as strings - return a.toString() < b.toString(); - } - - // Perform type-specific comparison -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - switch (static_cast(a.typeId())) { -#else - switch (static_cast(a.type())) { -#endif - case static_cast(QMetaType::Int): - return a.toInt() < b.toInt(); - case static_cast(QMetaType::Double): - return a.toDouble() < b.toDouble(); - case static_cast(QMetaType::QString): - return a.toString() < b.toString(); - case static_cast(QMetaType::Bool): - return a.toBool() < b.toBool(); - default: - // Default to comparing as strings - return a.toString() < b.toString(); - } -} - -QVariant CardInfoComparator::getProperty(const CardInfoPtr &card, const QString &property) const -{ - // Check if the property exists in the main fields of the class - if (property == "name") { - return card->getName(); - } else if (property == "text") { - return card->getText(); - } else if (property == "isToken") { - return card->getIsToken(); - } - - // Otherwise, check if it's a custom property in the QVariantHash - if (card->hasProperty(property)) { - return card->getProperty(property); - } - - return QVariant(); // Return an invalid variant if the property does not exist -} diff --git a/libcockatrice_card/libcockatrice/card/card_info_comparator.h b/libcockatrice_card/libcockatrice/card/card_info_comparator.h deleted file mode 100644 index b8d9c47e6..000000000 --- a/libcockatrice_card/libcockatrice/card/card_info_comparator.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file card_info_comparator.h - * @ingroup Cards - * @brief TODO: Document this. - */ - -#ifndef CARD_INFO_COMPARATOR_H -#define CARD_INFO_COMPARATOR_H - -#include "card_info.h" - -#include -#include - -class CardInfoComparator -{ -public: - explicit CardInfoComparator(const QStringList &properties, Qt::SortOrder order = Qt::AscendingOrder); - bool operator()(const CardInfoPtr &a, const CardInfoPtr &b) const; - -private: - QStringList m_properties; // List of properties to sort by - Qt::SortOrder m_order; - - [[nodiscard]] QVariant getProperty(const CardInfoPtr &card, const QString &property) const; - [[nodiscard]] bool compareVariants(const QVariant &a, const QVariant &b) const; -}; - -#endif // CARD_INFO_COMPARATOR_H diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.cpp b/libcockatrice_card/libcockatrice/card/database/card_database.cpp deleted file mode 100644 index 5c4b408b3..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "card_database.h" - -#include "../relation/card_relation.h" -#include "parser/cockatrice_xml_4.h" - -#include -#include -#include -#include -#include -#include -#include - -CardDatabase::CardDatabase(QObject *parent, - ICardPreferenceProvider *prefs, - ICardDatabasePathProvider *pathProvider, - ICardSetPriorityController *_setPriorityController) - : QObject(parent), setPriorityController(_setPriorityController), loadStatus(NotLoaded) -{ - qRegisterMetaType("CardInfoPtr"); - qRegisterMetaType("CardSetPtr"); - - // create loader and wire it up - loader = new CardDatabaseLoader(this, this, pathProvider, prefs, setPriorityController); - // re-emit loader signals (so other code doesn't need to know about internals) - connect(loader, &CardDatabaseLoader::loadingFinished, this, &CardDatabase::cardDatabaseLoadingFinished); - connect(loader, &CardDatabaseLoader::loadingFailed, this, &CardDatabase::cardDatabaseLoadingFailed); - connect(loader, &CardDatabaseLoader::newSetsFound, this, &CardDatabase::cardDatabaseNewSetsFound); - connect(loader, &CardDatabaseLoader::allNewSetsEnabled, this, &CardDatabase::cardDatabaseAllNewSetsEnabled); - - querier = new CardDatabaseQuerier(this, this, prefs); -} - -CardDatabase::~CardDatabase() -{ - clear(); -} - -void CardDatabase::clear() -{ - QMutexLocker locker(clearDatabaseMutex); - - for (const auto &card : cards.values()) { - if (card) { - removeCard(card); - } - } - - cards.clear(); - simpleNameCards.clear(); - - sets.clear(); - ICardDatabaseParser::clearSetlist(); - - loadStatus = NotLoaded; -} - -void CardDatabase::loadCardDatabases() -{ - loadStatus = loader->loadCardDatabases(); -} - -bool CardDatabase::saveCustomTokensToFile() -{ - return loader->saveCustomTokensToFile(); -} - -void CardDatabase::refreshCachedReverseRelatedCards() -{ - for (const auto &card : cards) { - card->resetReverseRelatedCards2Me(); - } - - for (const auto &card : cards) { - for (auto *rel : card->getReverseRelatedCards()) { - if (auto target = cards.value(rel->getName())) { - auto *newRel = new CardRelation(card->getName(), rel->getAttachType(), rel->getIsCreateAllExclusion(), - rel->getIsVariable(), rel->getDefaultCount(), rel->getIsPersistent()); - target->addReverseRelatedCards2Me(newRel); - } - } - } -} - -void CardDatabase::addCard(CardInfoPtr card) -{ - if (card == nullptr) { - qCWarning(CardDatabaseLog) << "CardDatabase::addCard(nullptr)"; - return; - } - - auto name = card->getName(); - - // If a card already exists, just add the new set property. - if (auto existing = cards.value(name)) { - for (const auto &printings : card->getSets()) - for (const auto &printing : printings) - existing->addToSet(printing.getSet(), printing); - return; - } - - QMutexLocker locker(addCardMutex); - cards.insert(name, card); - simpleNameCards.insert(card->getSimpleName(), card); - - emit cardAdded(card); -} - -void CardDatabase::removeCard(CardInfoPtr card) -{ - if (card.isNull()) { - qCWarning(CardDatabaseLog) << "CardDatabase::removeCard(nullptr)"; - return; - } - - for (auto *cardRelation : card->getRelatedCards()) - cardRelation->deleteLater(); - - for (auto *cardRelation : card->getReverseRelatedCards()) - cardRelation->deleteLater(); - - for (auto *cardRelation : card->getReverseRelatedCards2Me()) - cardRelation->deleteLater(); - - QMutexLocker locker(removeCardMutex); - cards.remove(card->getName()); - simpleNameCards.remove(card->getSimpleName()); - emit cardRemoved(card); -} - -void CardDatabase::addSet(CardSetPtr set) -{ - sets.insert(set->getShortName(), set); -} - -CardSetPtr CardDatabase::getSet(const QString &setName) -{ - if (sets.contains(setName)) { - return sets.value(setName); - } else { - CardSetPtr newSet = CardSet::newInstance(setPriorityController, setName); - sets.insert(setName, newSet); - return newSet; - } -} - -CardSetList CardDatabase::getSetList() const -{ - CardSetList result; - for (auto set : sets.values()) { - result << set; - } - return result; -} - -void CardDatabase::checkUnknownSets() -{ - auto _sets = getSetList(); - - if (_sets.getEnabledSetsNum()) { - // if some sets are first found on this run, ask the user - int numUnknownSets = _sets.getUnknownSetsNum(); - QStringList unknownSetNames = _sets.getUnknownSetsNames(); - if (numUnknownSets > 0) { - emit cardDatabaseNewSetsFound(numUnknownSets, unknownSetNames); - } else { - _sets.markAllAsKnown(); - } - } else { - // No set enabled. Probably this is the first time running trice - _sets.guessSortKeys(); - _sets.sortByKey(); - _sets.enableAll(); - notifyEnabledSetsChanged(); - - emit cardDatabaseAllNewSetsEnabled(); - } -} - -void CardDatabase::enableAllUnknownSets() -{ - auto _sets = getSetList(); - _sets.enableAllUnknown(); -} - -void CardDatabase::markAllSetsAsKnown() -{ - auto _sets = getSetList(); - _sets.markAllAsKnown(); -} - -void CardDatabase::notifyEnabledSetsChanged() -{ - // refresh the list of cached set names - for (const CardInfoPtr &card : cards) { - card->refreshCachedSets(); - } - - // inform the carddatabasemodels that they need to re-check their list of cards - emit cardDatabaseEnabledSetsChanged(); -} - -void CardDatabase::addFormat(FormatRulesPtr format) -{ - formats.insert(format->formatName.toLower(), format); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/database/card_database.h b/libcockatrice_card/libcockatrice/card/database/card_database.h deleted file mode 100644 index 7f8fc39db..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef CARDDATABASE_H -#define CARDDATABASE_H - -#include "../set/card_set_list.h" -#include "card_database_loader.h" -#include "card_database_querier.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(CardDatabaseLog, "card_database"); - -/** - * @class CardDatabase - * @ingroup CardDatabase - * @brief Core in-memory container for card and set data. - * - * Responsible for maintaining CardInfo objects, CardSet objects, and - * providing access to CardDatabaseQuerier for query operations. - * Handles addition, removal, and clearing of cards and sets. - */ -class CardDatabase : public QObject -{ - Q_OBJECT - -protected: - /// Controller to determine set priority when choosing preferred printings. - ICardSetPriorityController *setPriorityController; - - /// Cards indexed by exact name - CardNameMap cards; - - /// Cards indexed by simplified name (normalized) - CardNameMap simpleNameCards; - - /// Sets indexed by short name - SetNameMap sets; - - FormatRulesNameMap formats; - - /// Loader responsible for file discovery and parsing - CardDatabaseLoader *loader; - - /// Current load status of the database - LoadStatus loadStatus; - - /// Querier for higher-level card lookups - CardDatabaseQuerier *querier; - -private: - /** - * @brief Check for sets that are unknown and emit signals if needed. - */ - void checkUnknownSets(); - - /** - * @brief Refreshes the cached reverse-related cards for all cards. - */ - void refreshCachedReverseRelatedCards(); - - /// Mutexes for thread safety - QBasicMutex *clearDatabaseMutex = new QBasicMutex(), *addCardMutex = new QBasicMutex(), - *removeCardMutex = new QBasicMutex(); - -public: - /** - * @brief Constructs a new CardDatabase instance. - * @param parent QObject parent. - * @param prefs Optional card preference provider. - * @param pathProvider Optional database path provider. - * @param setPriorityController Optional controller for set priority. - */ - explicit CardDatabase(QObject *parent = nullptr, - ICardPreferenceProvider *prefs = nullptr, - ICardDatabasePathProvider *pathProvider = nullptr, - ICardSetPriorityController *setPriorityController = nullptr); - - /** @brief Destructor clears all internal data. */ - ~CardDatabase() override; - - /** - * @brief Removes a card from the database. - * @param card Pointer to the card to remove. - */ - void removeCard(CardInfoPtr card); - - /** @brief Clears all cards, sets, and internal state. */ - void clear(); - - /** @brief Returns the map of cards by name. */ - [[nodiscard]] const CardNameMap &getCardList() const - { - return cards; - } - - /** - * @brief Retrieves a set by short name, creating a new one if missing. - * @param setName Short name of the set. - * @return Pointer to the CardSet. - */ - CardSetPtr getSet(const QString &setName); - - /** @brief Returns a list of all sets in the database. */ - [[nodiscard]] CardSetList getSetList() const; - - /** @brief Returns the current load status. */ - [[nodiscard]] LoadStatus getLoadStatus() const - { - return loadStatus; - } - - /** @brief Returns the querier for performing card lookups. */ - [[nodiscard]] CardDatabaseQuerier *query() const - { - return querier; - } - - /** @brief Enables all unknown sets in the database. */ - void enableAllUnknownSets(); - - /** @brief Marks all sets as known. */ - void markAllSetsAsKnown(); - - /** @brief Notifies listeners that enabled sets changed. */ - void notifyEnabledSetsChanged(); - -public slots: - /** - * @brief Adds a card to the database. - * @param card CardInfoPtr to add. - */ - void addCard(CardInfoPtr card); - - /** - * @brief Adds a set to the database. - * @param set Pointer to CardSet to add. - */ - void addSet(CardSetPtr set); - - void addFormat(FormatRulesPtr format); - - /** @brief Loads card databases from configured paths. */ - void loadCardDatabases(); - - /** @brief Saves custom tokens to file. - * @return True if successful. - */ - bool saveCustomTokensToFile(); - -signals: - /** @brief Emitted when the card database has finished loading successfully. */ - void cardDatabaseLoadingFinished(); - - /** @brief Emitted when the card database fails to load. */ - void cardDatabaseLoadingFailed(); - - /** - * @brief Emitted when new sets are found. - * @param numUnknownSets Number of unknown sets. - * @param unknownSetsNames Names of unknown sets. - */ - void cardDatabaseNewSetsFound(int numUnknownSets, QStringList unknownSetsNames); - - /** @brief Emitted when all new sets have been enabled. */ - void cardDatabaseAllNewSetsEnabled(); - - /** @brief Emitted when enabled sets have changed. */ - void cardDatabaseEnabledSetsChanged(); - - /** @brief Emitted when a new card is added. */ - void cardAdded(CardInfoPtr card); - - /** @brief Emitted when a card is removed. */ - void cardRemoved(CardInfoPtr card); - - friend class CardDatabaseLoader; - friend class CardDatabaseQuerier; -}; - -#endif // CARDDATABASE_H diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp deleted file mode 100644 index 716477a59..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "card_database_loader.h" - -#include "card_database.h" -#include "parser/cockatrice_xml_3.h" -#include "parser/cockatrice_xml_4.h" - -#include -#include -#include -#include - -CardDatabaseLoader::CardDatabaseLoader(QObject *parent, - CardDatabase *db, - ICardDatabasePathProvider *_pathProvider, - ICardPreferenceProvider *_preferenceProvider, - ICardSetPriorityController *_priorityController) - : QObject(parent), database(db), pathProvider(_pathProvider) -{ - // instantiate available parsers here and connect them to the database - availableParsers << new CockatriceXml4Parser(_preferenceProvider, _priorityController); - availableParsers << new CockatriceXml3Parser(_priorityController); - - for (auto *p : availableParsers) { - // connect parser outputs to the database adders - connect(p, &ICardDatabaseParser::addCard, database, &CardDatabase::addCard, Qt::DirectConnection); - connect(p, &ICardDatabaseParser::addSet, database, &CardDatabase::addSet, Qt::DirectConnection); - connect(p, &ICardDatabaseParser::addFormat, database, &CardDatabase::addFormat, Qt::DirectConnection); - } - - // when SettingsCache's path changes, trigger reloads - connect(pathProvider, &ICardDatabasePathProvider::cardDatabasePathChanged, this, - &CardDatabaseLoader::loadCardDatabases); -} - -CardDatabaseLoader::~CardDatabaseLoader() -{ - qDeleteAll(availableParsers); - availableParsers.clear(); -} - -LoadStatus CardDatabaseLoader::loadFromFile(const QString &fileName) -{ - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - return FileError; - } - - for (auto parser : availableParsers) { - file.reset(); - if (parser->getCanParseFile(fileName, file)) { - file.reset(); - parser->parseFile(file); - return Ok; - } - } - - return Invalid; -} - -LoadStatus CardDatabaseLoader::loadCardDatabase(const QString &path) -{ - auto startTime = QTime::currentTime(); - LoadStatus tempLoadStatus = NotLoaded; - if (!path.isEmpty()) { - QMutexLocker locker(loadFromFileMutex); - tempLoadStatus = loadFromFile(path); - } - - int msecs = startTime.msecsTo(QTime::currentTime()); - qCInfo(CardDatabaseLoadingLog) << "Loaded card database: Path =" << path << "Status =" << tempLoadStatus - << "Cards =" << (database ? database->cards.size() : 0) - << "Sets =" << (database ? database->sets.size() : 0) << QString("%1ms").arg(msecs); - - return tempLoadStatus; -} - -LoadStatus CardDatabaseLoader::loadCardDatabases() -{ - QMutexLocker locker(reloadDatabaseMutex); - - if (!database) { - qCWarning(CardDatabaseLoadingLog) << "Loader has no database pointer"; - emit loadingFailed(); - return FileError; - } - emit loadingStarted(); - qCInfo(CardDatabaseLoadingLog) << "Card Database Loading Started"; - - database->clear(); // remove old db - - LoadStatus loadStatus = loadCardDatabase(pathProvider->getCardDatabasePath()); // load main card database - loadCardDatabase(pathProvider->getTokenDatabasePath()); // load tokens database - loadCardDatabase(pathProvider->getSpoilerCardDatabasePath()); // load spoilers database - - // find all custom card databases, recursively & following symlinks - // then load them alphabetically - const QStringList customPaths = collectCustomDatabasePaths(); - for (int i = 0; i < customPaths.size(); ++i) { - const auto &p = customPaths.at(i); - qCInfo(CardDatabaseLoadingLog) << "Loading Custom Set" << i << "(" << p << ")"; - loadCardDatabase(p); - } - - // AFTER all the cards have been loaded - - // resolve the reverse-related tags - - database->refreshCachedReverseRelatedCards(); - - if (loadStatus == Ok) { - database->checkUnknownSets(); // update deck editors, etc - qCInfo(CardDatabaseLoadingSuccessOrFailureLog) << "Card Database Loading Success"; - emit loadingFinished(); - } else { - qCInfo(CardDatabaseLoadingSuccessOrFailureLog) << "Card Database Loading Failed"; - emit loadingFailed(); // bring up the settings dialog - } - - return loadStatus; -} - -QStringList CardDatabaseLoader::collectCustomDatabasePaths() const -{ - QDirIterator it(pathProvider->getCustomCardDatabasePath(), {"*.xml"}, QDir::Files, - QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); - - QStringList paths; - while (it.hasNext()) - paths << it.next(); - paths.sort(); - return paths; -} - -bool CardDatabaseLoader::saveCustomTokensToFile() -{ - if (!database) { - qCWarning(CardDatabaseLog) << "saveCustomTokensToFile: database pointer missing"; - return false; - } - - QString fileName = pathProvider->getCustomCardDatabasePath() + "/" + CardSet::TOKENS_SETNAME + ".xml"; - - SetNameMap tmpSets; - CardSetPtr customTokensSet = database->getSet(CardSet::TOKENS_SETNAME); - tmpSets.insert(CardSet::TOKENS_SETNAME, customTokensSet); - - CardNameMap tmpCards; - for (const CardInfoPtr &card : database->cards) { - if (card->getSets().contains(CardSet::TOKENS_SETNAME)) { - tmpCards.insert(card->getName(), card); - } - } - - availableParsers.first()->saveToFile(FormatRulesNameMap(), tmpSets, tmpCards, fileName); - return true; -} diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h b/libcockatrice_card/libcockatrice/card/database/card_database_loader.h deleted file mode 100644 index 861cc95b0..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_loader.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef COCKATRICE_CARD_DATABASE_LOADER_H -#define COCKATRICE_CARD_DATABASE_LOADER_H - -#include -#include -#include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(CardDatabaseLoadingLog, "card_database.loading"); -inline Q_LOGGING_CATEGORY(CardDatabaseLoadingSuccessOrFailureLog, "card_database.loading.success_or_failure"); - -class CardDatabase; -class ICardDatabaseParser; - -/** - * @enum LoadStatus - * @brief Represents the result of attempting to load a card database. - */ -enum LoadStatus -{ - Ok, /**< Database loaded successfully. */ - VersionTooOld, /**< Database version is too old to load. */ - Invalid, /**< Database is invalid or unparsable. */ - NotLoaded, /**< Database has not been loaded. */ - FileError, /**< Error opening or reading the file. */ - NoCards /**< Database contains no cards. */ -}; - -/** - * @class CardDatabaseLoader - * @ingroup CardDatabase - * @brief Handles loading card databases from disk and saving custom tokens. - * - * This class is responsible for: - * - Discovering configured card database paths. - * - Loading main, token, spoiler, and custom databases. - * - Populating a CardDatabase instance using connected parsers. - * - Emitting signals about loading progress and new sets. - */ -class CardDatabaseLoader : public QObject -{ - Q_OBJECT -public: - /** - * @brief Constructs a CardDatabaseLoader. - * @param parent QObject parent. - * @param db Pointer to the CardDatabase to populate (non-owning). - * @param pathProvider Provider for card database file paths. - * @param preferenceProvider Optional card preference provider for pinned printings. - */ - explicit CardDatabaseLoader(QObject *parent, - CardDatabase *db, - ICardDatabasePathProvider *pathProvider, - ICardPreferenceProvider *preferenceProvider, - ICardSetPriorityController *_priorityController); - - /** @brief Destructor cleans up allocated parsers. */ - ~CardDatabaseLoader() override; - -public slots: - /** - * @brief Loads all configured card databases. - * @return Status of the main database load. - */ - LoadStatus loadCardDatabases(); - - /** - * @brief Loads a single card database file. - * @param path Path to the database file. - * @return LoadStatus indicating success or failure. - */ - LoadStatus loadCardDatabase(const QString &path); - - /** - * @brief Saves custom tokens to the user-defined custom database path. - * @return True if the save was successful. - */ - bool saveCustomTokensToFile(); - -signals: - /** @brief Emitted when loading starts. */ - void loadingStarted(); - - /** @brief Emitted when loading finishes successfully. */ - void loadingFinished(); - - /** @brief Emitted when loading fails. */ - void loadingFailed(); - - /** - * @brief Emitted when new sets are discovered during loading. - * @param numSets Number of new sets. - * @param setNames Names of the discovered sets. - */ - void newSetsFound(int numSets, const QStringList &setNames); - - /** @brief Emitted when all newly discovered sets have been enabled. */ - void allNewSetsEnabled(); - -private: - /** - * @brief Loads a database from a single file using the available parsers. - * @param fileName Path to the database file. - * @return LoadStatus indicating success or failure. - */ - LoadStatus loadFromFile(const QString &fileName); - - /** - * @brief Collects custom card database paths recursively. - * @return Sorted list of file paths to custom databases. - */ - [[nodiscard]] QStringList collectCustomDatabasePaths() const; - -private: - CardDatabase *database; /**< Non-owning pointer to the target CardDatabase. */ - ICardDatabasePathProvider *pathProvider; /**< Pointer to the path provider. */ - QList availableParsers; /**< List of available parsers for different formats. */ - - QBasicMutex *loadFromFileMutex = new QBasicMutex(); /**< Mutex for single-file loading. */ - QBasicMutex *reloadDatabaseMutex = new QBasicMutex(); /**< Mutex for reloading entire database. */ -}; - -#endif // COCKATRICE_CARD_DATABASE_LOADER_H diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_manager.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_manager.cpp deleted file mode 100644 index 614b4a7f8..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_manager.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "card_database_manager.h" - -#include -#include -#include - -ICardPreferenceProvider *CardDatabaseManager::cardPreferenceProvider = new NoopCardPreferenceProvider(); -ICardDatabasePathProvider *CardDatabaseManager::pathProvider = new NoopCardDatabasePathProvider(); -ICardSetPriorityController *CardDatabaseManager::setPriorityController = new NoopCardSetPriorityController(); - -void CardDatabaseManager::setCardPreferenceProvider(ICardPreferenceProvider *provider) -{ - cardPreferenceProvider = provider; -} - -void CardDatabaseManager::setCardDatabasePathProvider(ICardDatabasePathProvider *provider) -{ - pathProvider = provider; -} - -void CardDatabaseManager::setCardSetPriorityController(ICardSetPriorityController *controller) -{ - setPriorityController = controller; -} - -CardDatabase *CardDatabaseManager::getInstance() -{ - static CardDatabase instance(nullptr, cardPreferenceProvider, pathProvider, setPriorityController); - return &instance; -} - -CardDatabaseQuerier *CardDatabaseManager::query() -{ - return getInstance()->query(); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_manager.h b/libcockatrice_card/libcockatrice/card/database/card_database_manager.h deleted file mode 100644 index 58a744fbb..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_manager.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef CARD_DATABASE_ACCESSOR_H -#define CARD_DATABASE_ACCESSOR_H - -#pragma once -#include "card_database.h" - -/** - * @class CardDatabaseManager - * @ingroup CardDatabase - * @brief The CardDatabaseManager is responsible for managing the global CardDatabase singleton. - * - * This class provides a static interface for accessing the global CardDatabase instance - * and its CardDatabaseQuerier. It also allows the configuration of optional providers: - * - ICardPreferenceProvider - * - ICardDatabasePathProvider - * - ICardSetPriorityController - * - * Only a single instance of CardDatabase exists, enforced via a private constructor and - * deleted copy/move operations. - */ -class CardDatabaseManager -{ -public: - /** @brief Deleted copy constructor to enforce singleton. */ - CardDatabaseManager(const CardDatabaseManager &) = delete; - - /** @brief Deleted assignment operator to enforce singleton. */ - CardDatabaseManager &operator=(const CardDatabaseManager &) = delete; - - /** - * @brief Sets the card preference provider. - * @param provider Pointer to an ICardPreferenceProvider. - * @note Must be called before the first call to getInstance(). - */ - static void setCardPreferenceProvider(ICardPreferenceProvider *provider); - - /** - * @brief Sets the card database path provider. - * @param provider Pointer to an ICardDatabasePathProvider. - * @note Must be called before the first call to getInstance(). - */ - static void setCardDatabasePathProvider(ICardDatabasePathProvider *provider); - - /** - * @brief Sets the card set priority controller. - * @param controller Pointer to an ICardSetPriorityController. - * @note Must be called before the first call to getInstance(). - */ - static void setCardSetPriorityController(ICardSetPriorityController *controller); - - /** - * @brief Returns the singleton CardDatabase instance. - * @return Pointer to the global CardDatabase. - */ - static CardDatabase *getInstance(); - - /** - * @brief Returns the CardDatabaseQuerier of the singleton database. - * @return Pointer to CardDatabaseQuerier. - */ - static CardDatabaseQuerier *query(); - -private: - /** @brief Private default constructor to enforce singleton. */ - CardDatabaseManager() = default; - - /** @brief Private destructor. */ - ~CardDatabaseManager() = default; - - /// Static card preference provider pointer (default: Noop) - static ICardPreferenceProvider *cardPreferenceProvider; - - /// Static path provider pointer (default: Noop) - static ICardDatabasePathProvider *pathProvider; - - /// Static set priority controller pointer (default: Noop) - static ICardSetPriorityController *setPriorityController; -}; - -#endif // CARD_DATABASE_ACCESSOR_H diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp b/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp deleted file mode 100644 index 021e8d12d..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.cpp +++ /dev/null @@ -1,363 +0,0 @@ -#include "card_database_querier.h" - -#include "../card_info.h" -#include "../printing/exact_card.h" -#include "../set/card_set_comparator.h" -#include "card_database.h" - -#include - -CardDatabaseQuerier::CardDatabaseQuerier(QObject *_parent, - const CardDatabase *_db, - const ICardPreferenceProvider *prefs) - : QObject(_parent), db(_db), prefs(prefs) -{ -} - -/** - * Looks up the cardInfo corresponding to the cardName. - * - * @param cardName The card name to look up - * @return A CardInfoPtr, or null if not corresponding CardInfo is found. - */ -CardInfoPtr CardDatabaseQuerier::getCardInfo(const QString &cardName) const -{ - return db->cards.value(cardName); -} - -/** - * Looks up the cardInfos for a list of card names. - * - * @param cardNames The card names to look up - * @return A List of CardInfoPtr. Any failed lookups will be ignored and dropped from the resulting list - */ -QList CardDatabaseQuerier::getCardInfos(const QStringList &cardNames) const -{ - QList cardInfos; - for (const QString &cardName : cardNames) { - CardInfoPtr ptr = db->cards.value(cardName); - if (ptr) - cardInfos.append(ptr); - } - - return cardInfos; -} - -CardInfoPtr CardDatabaseQuerier::getCardBySimpleName(const QString &cardName) const -{ - return db->simpleNameCards.value(CardInfo::simplifyName(cardName)); -} - -CardInfoPtr CardDatabaseQuerier::lookupCardByName(const QString &name) const -{ - if (auto info = getCardInfo(name)) - return info; - if (auto info = getCardBySimpleName(name)) - return info; - return getCardBySimpleName(CardInfo::simplifyName(name)); -} - -/** - * Looks up the cards corresponding to the CardRefs. - * If the providerId is empty, will default to the preferred printing. - * If providerId is given but not found, the PrintingInfo will be empty. - * - * @param cardRefs The cards to look up. If providerId is empty for an entry, will default to the preferred printing for - * that entry. If providerId is given but not found, the PrintingInfo will be empty for that entry. - * @return A list of cards. Any failed lookups will be ignored and dropped from the resulting list. - */ -QList CardDatabaseQuerier::getCards(const QList &cardRefs) const -{ - QList cards; - for (const auto &cardRef : cardRefs) { - ExactCard card = getCard(cardRef); - if (card) - cards.append(card); - } - - return cards; -} - -/** - * Looks up the card corresponding to the CardRef. - * If the providerId is empty, will default to the preferred printing. - * If providerId is given but not found, the PrintingInfo will be empty. - * - * @param cardRef The card to look up. - * @return A specific printing of a card, or empty if not found. - */ -ExactCard CardDatabaseQuerier::getCard(const CardRef &cardRef) const -{ - auto info = getCardInfo(cardRef.name); - if (info.isNull()) { - return {}; - } - - if (cardRef.providerId.isEmpty() || cardRef.providerId.isNull()) { - return ExactCard(info, getPreferredPrinting(info)); - } - - return ExactCard(info, findPrintingWithId(info, cardRef.providerId)); -} - -/** - * Looks up the card by CardRef, simplifying the name if required. - * If the providerId is empty, will default to the preferred printing. - * If providerId is given but not found, the PrintingInfo will be empty. - * - * @param cardRef The card to look up. - * @return A specific printing of a card, or empty if not found. - */ -ExactCard CardDatabaseQuerier::guessCard(const CardRef &cardRef) const -{ - auto card = lookupCardByName(cardRef.name); - auto printing = - cardRef.providerId.isEmpty() ? getPreferredPrinting(card) : findPrintingWithId(card, cardRef.providerId); - - return ExactCard(card, printing); -} - -ExactCard CardDatabaseQuerier::getRandomCard() const -{ - if (db->cards.isEmpty()) - return {}; - - const auto keys = db->cards.keys(); - int randomIndex = QRandomGenerator::global()->bounded(keys.size()); - const QString &randomKey = keys.at(randomIndex); - CardInfoPtr randomCard = getCardInfo(randomKey); - - return ExactCard{randomCard, getPreferredPrinting(randomCard)}; -} - -ExactCard CardDatabaseQuerier::getCardFromSameSet(const QString &cardName, const PrintingInfo &otherPrinting) const -{ - // The source card does not have a printing defined, which means we can't get a card from the same set. - if (otherPrinting.isEmpty()) { - return getCard({cardName}); - } - - // The source card does have a printing defined, which means we can attempt to get a card from the same set. - PrintingInfo relatedPrinting = getSpecificPrinting(cardName, otherPrinting.getSet()->getCorrectedShortName(), ""); - ExactCard relatedCard(guessCard({cardName}).getCardPtr(), relatedPrinting); - - // If we didn't find a card from the same set, just try to find any card with the same name. - return relatedCard ? relatedCard : getCard({cardName}); -} - -/** - * Finds the PrintingInfo in the cardInfo that has the given uuid field. - * - * @param cardInfo The CardInfo to search - * @param providerId The uuid to look for - * @return The PrintingInfo, or a default-constructed PrintingInfo if not found. - */ -PrintingInfo CardDatabaseQuerier::findPrintingWithId(const CardInfoPtr &cardInfo, const QString &providerId) const -{ - for (const auto &printings : cardInfo->getSets()) { - for (const auto &printing : printings) { - if (printing.getUuid() == providerId) { - return printing; - } - } - } - - return PrintingInfo(); -} - -PrintingInfo CardDatabaseQuerier::getSpecificPrinting(const CardRef &cardRef) const -{ - CardInfoPtr cardInfo = getCardInfo(cardRef.name); - if (!cardInfo) { - return PrintingInfo(nullptr); - } - - return findPrintingWithId(cardInfo, cardRef.providerId); -} - -PrintingInfo CardDatabaseQuerier::getSpecificPrinting(const QString &cardName, - const QString &setShortName, - const QString &collectorNumber) const -{ - CardInfoPtr cardInfo = getCardInfo(cardName); - if (!cardInfo) { - return PrintingInfo(nullptr); - } - - SetToPrintingsMap setMap = cardInfo->getSets(); - if (setMap.empty()) { - return PrintingInfo(nullptr); - } - - for (const auto &printings : setMap) { - for (auto &cardInfoForSet : printings) { - if (!collectorNumber.isEmpty()) { - if (cardInfoForSet.getSet()->getShortName() == setShortName && - cardInfoForSet.getProperty("num") == collectorNumber) { - return cardInfoForSet; - } - } else { - if (cardInfoForSet.getSet()->getShortName() == setShortName) { - return cardInfoForSet; - } - } - } - } - - return PrintingInfo(nullptr); -} - -/** - * Gets the card representing the preferred printing of the cardInfo - * - * @param cardName The cardName to find the preferred card and printing for - * @return A specific printing of a card - */ -ExactCard CardDatabaseQuerier::getPreferredCard(const QString &cardName) const -{ - return getPreferredCard(getCardInfo(cardName)); -} - -/** - * Gets the card representing the preferred printing of the cardInfo - * - * @param cardInfo The cardInfo to find the preferred printing for - * @return A specific printing of a card - */ -ExactCard CardDatabaseQuerier::getPreferredCard(const CardInfoPtr &cardInfo) const -{ - return ExactCard(cardInfo, getPreferredPrinting(cardInfo)); -} - -bool CardDatabaseQuerier::isPreferredPrinting(const CardRef &cardRef) const -{ - if (cardRef.providerId.startsWith("card_")) { - return cardRef.providerId == - QLatin1String("card_") + cardRef.name + QString("_") + getPreferredPrintingProviderId(cardRef.name); - } - return cardRef.providerId == getPreferredPrintingProviderId(cardRef.name); -} - -PrintingInfo CardDatabaseQuerier::getPreferredPrinting(const QString &cardName) const -{ - CardInfoPtr cardInfo = getCardInfo(cardName); - return getPreferredPrinting(cardInfo); -} - -PrintingInfo CardDatabaseQuerier::getPreferredPrinting(const CardInfoPtr &cardInfo) const -{ - if (!cardInfo) { - return PrintingInfo(nullptr); - } - - const auto &pinnedPrintingProviderId = prefs->getCardPreferenceOverride(cardInfo->getName()); - - if (!pinnedPrintingProviderId.isEmpty()) { - return getSpecificPrinting({cardInfo->getName(), pinnedPrintingProviderId}); - } - - SetToPrintingsMap setMap = cardInfo->getSets(); - if (setMap.empty()) { - return PrintingInfo(nullptr); - } - - CardSetPtr preferredSet = nullptr; - PrintingInfo preferredPrinting; - SetPriorityComparator comparator; - - for (const auto &printings : setMap) { - for (auto &printing : printings) { - CardSetPtr currentSet = printing.getSet(); - if (!preferredSet || comparator(currentSet, preferredSet)) { - preferredSet = currentSet; - preferredPrinting = printing; - } - } - } - - if (preferredSet) { - return preferredPrinting; - } - - return PrintingInfo(nullptr); -} - -QString CardDatabaseQuerier::getPreferredPrintingProviderId(const QString &cardName) const -{ - PrintingInfo preferredPrinting = getPreferredPrinting(cardName); - QString uuid = preferredPrinting.getUuid(); - if (!uuid.isEmpty()) { - return uuid; - } - - CardInfoPtr defaultCardInfo = getCardInfo(cardName); - if (defaultCardInfo.isNull()) { - return cardName; - } - return defaultCardInfo->getName(); -} - -QStringList CardDatabaseQuerier::getAllMainCardTypes() const -{ - QSet types; - for (const auto &card : db->cards.values()) { - types.insert(card->getMainCardType()); - } - return types.values(); -} - -QMap CardDatabaseQuerier::getAllMainCardTypesWithCount() const -{ - QMap typeCounts; - - for (const auto &card : db->cards.values()) { - QString type = card->getMainCardType(); - typeCounts[type]++; - } - - return typeCounts; -} - -QMap CardDatabaseQuerier::getAllSubCardTypesWithCount() const -{ - QMap typeCounts; - - for (const auto &card : db->cards.values()) { - QString type = card->getCardType(); - - QStringList parts = type.split(" — "); - - if (parts.size() > 1) { // Ensure there are subtypes - QStringList subtypes = parts[1].split(" ", Qt::SkipEmptyParts); - - for (const QString &subtype : subtypes) { - typeCounts[subtype]++; - } - } - } - - return typeCounts; -} - -FormatRulesPtr CardDatabaseQuerier::getFormat(const QString &formatName) const -{ - return db->formats.value(formatName.toLower()); -} - -QMap CardDatabaseQuerier::getAllFormatsWithCount() const -{ - QMap formatCounts; - - for (const auto &card : db->cards.values()) { - QStringList allProps = card->getProperties(); - - for (const QString &prop : allProps) { - if (prop.startsWith("format-")) { - QString formatName = prop.mid(QStringLiteral("format-").size()); - formatCounts[formatName]++; - } - } - } - - return formatCounts; -} diff --git a/libcockatrice_card/libcockatrice/card/database/card_database_querier.h b/libcockatrice_card/libcockatrice/card/database/card_database_querier.h deleted file mode 100644 index ff8d7958b..000000000 --- a/libcockatrice_card/libcockatrice/card/database/card_database_querier.h +++ /dev/null @@ -1,225 +0,0 @@ -#ifndef COCKATRICE_CARD_DATABASE_QUERIER_H -#define COCKATRICE_CARD_DATABASE_QUERIER_H - -#include "../card_info.h" -#include "../printing/exact_card.h" - -#include -#include -#include - -class CardDatabase; - -/** - * @class CardDatabaseQuerier - * @ingroup CardDatabase - * @brief Provides lookup and convenience functions for querying cards and their printings. - * - * The CardDatabaseQuerier class offers various lookup helpers for retrieving card information - * (e.g., CardInfoPtr, ExactCard, and PrintingInfo) from a CardDatabase. It also applies user - * printing preferences via ICardPreferenceProvider when determining preferred printings. - */ -class CardDatabaseQuerier : public QObject -{ - Q_OBJECT - -public: - /** - * @brief Constructs a CardDatabaseQuerier. - * - * @param parent Parent QObject. - * @param db Pointer to the CardDatabase used for lookups. - * @param prefs Pointer to card preference provider which supplies user-preference for printings. - */ - explicit CardDatabaseQuerier(QObject *parent, const CardDatabase *db, const ICardPreferenceProvider *prefs); - - /** - * @brief Retrieves a card by its exact name. - * - * @param cardName Exact card name. - * @return A CardInfoPtr, or null if no matching card exists. - */ - [[nodiscard]] CardInfoPtr getCardInfo(const QString &cardName) const; - - /** - * @brief Retrieves multiple cards by their exact names. - * - * Failed lookups are skipped and not included in the result. - * - * @param cardNames List of exact card names. - * @return List of CardInfoPtr objects for which a match was found. - */ - [[nodiscard]] QList getCardInfos(const QStringList &cardNames) const; - - /** - * @brief Retrieves a card using simplified name matching. - * - * The name is automatically normalized, so callers do not need to simplify it. - * - * @param cardName A (possibly simplified or misspelled) card name. - * @return A CardInfoPtr, or null if not found. - */ - [[nodiscard]] CardInfoPtr getCardBySimpleName(const QString &cardName) const; - - /** - * @brief Looks up a card using exact name first, then simplified matching as fallback. - * - * @param name Raw card name input. - * @return The best-match CardInfoPtr, or null if no match is found. - */ - [[nodiscard]] CardInfoPtr lookupCardByName(const QString &name) const; - - /** - * @brief Converts a CardRef into an ExactCard. - * - * If the providerId is empty, the preferred printing is used. - * If providerId exists but cannot be found, an ExactCard with an empty PrintingInfo is returned. - * - * @param cardRef Card reference with name and optional providerId. - * @return The resolved ExactCard, or empty if no card was found. - */ - [[nodiscard]] ExactCard getCard(const CardRef &cardRef) const; - - /** - * @brief Resolves multiple CardRefs into ExactCards. - * - * Failed entries are not included in the result. - * - * @param cardRefs List of card references. - * @return List of successfully resolved ExactCards. - */ - [[nodiscard]] QList getCards(const QList &cardRefs) const; - - /** - * @brief Attempts a more flexible card lookup using both simple name matching and CardRef rules. - * - * If providerId is missing, uses preferred printing. If lookup fails, attempts simplified name. - * - * @param cardRef Card reference to resolve. - * @return The best-guess ExactCard, or empty if unresolved. - */ - [[nodiscard]] ExactCard guessCard(const CardRef &cardRef) const; - - /** - * @brief Returns a random card from the database using the preferred printing. - * - * @return A random ExactCard, or empty if the database is empty. - */ - [[nodiscard]] ExactCard getRandomCard() const; - - /** - * @brief Returns a printing of a card from the same set as another given printing when possible. - * - * If no matching printing exists, falls back to a standard lookup. - * - * @param cardName Card to retrieve. - * @param otherPrinting Printing to match the set against. - * @return Matching ExactCard if found, otherwise fallback ExactCard. - */ - [[nodiscard]] ExactCard getCardFromSameSet(const QString &cardName, const PrintingInfo &otherPrinting) const; - - /** - * @brief Returns the preferred printing of a card based on user preferences and set priority. - * - * @param cardName Name of the card. - * @return The preferred ExactCard. - */ - [[nodiscard]] ExactCard getPreferredCard(const QString &cardName) const; - - /** - * @brief Returns the preferred printing of a card based on user preferences and set priority. - * - * @param cardInfo Card information object. - * @return The preferred ExactCard. - */ - [[nodiscard]] ExactCard getPreferredCard(const CardInfoPtr &cardInfo) const; - - /** - * @brief Checks whether the CardRef refers to the preferred printing. - * - * @param cardRef Card reference to test. - * @return True if providerId matches the preferred printing. - */ - [[nodiscard]] bool isPreferredPrinting(const CardRef &cardRef) const; - - /** - * @brief Returns the preferred printing for the given card name. - * - * @param cardName Card name. - * @return Preferred PrintingInfo, or empty if not found. - */ - [[nodiscard]] PrintingInfo getPreferredPrinting(const QString &cardName) const; - - /** - * @brief Returns the preferred printing for the given card. - * - * @param cardInfo Card information object. - * @return Preferred PrintingInfo, or empty if not applicable. - */ - [[nodiscard]] PrintingInfo getPreferredPrinting(const CardInfoPtr &cardInfo) const; - - /** - * @brief Returns the providerId of the preferred printing. - * - * @param cardName Card name. - * @return ProviderId string for preferred printing. - */ - [[nodiscard]] QString getPreferredPrintingProviderId(const QString &cardName) const; - - /** - * @brief Retrieves a specific printing referenced by CardRef. - * - * @param cardRef Card reference including providerId. - * @return Matching PrintingInfo, or empty if not found. - */ - [[nodiscard]] PrintingInfo getSpecificPrinting(const CardRef &cardRef) const; - - /** - * @brief Searches for a specific printing by set code and collector number. - * - * @param cardName Card name to search. - * @param setCode Set (short) code to match. - * @param collectorNumber Collector number. If empty, any printing from the set is returned. - * @return Matching PrintingInfo, or empty if not found. - */ - [[nodiscard]] PrintingInfo - getSpecificPrinting(const QString &cardName, const QString &setCode, const QString &collectorNumber) const; - - /** - * @brief Searches for a printing that matches a given providerId. - * - * @param card Card to search. - * @param providerId Provider identifier to match. - * @return Matching PrintingInfo, or empty if not found. - */ - [[nodiscard]] PrintingInfo findPrintingWithId(const CardInfoPtr &card, const QString &providerId) const; - - /** - * @brief Returns a list of all main card types present in the database. - * - * @return List of main card type strings. - */ - [[nodiscard]] QStringList getAllMainCardTypes() const; - - /** - * @brief Returns a mapping of main card types to their occurrence counts. - * - * @return Map of main card type to count. - */ - [[nodiscard]] QMap getAllMainCardTypesWithCount() const; - - /** - * @brief Returns a mapping of card subtypes to their occurrence counts. - * - * @return Map of subtype string to count. - */ - [[nodiscard]] QMap getAllSubCardTypesWithCount() const; - FormatRulesPtr getFormat(const QString &formatName) const; - QMap getAllFormatsWithCount() const; - -private: - const CardDatabase *db; //!< Card database used for all lookups. - const ICardPreferenceProvider *prefs; //!< Preference provider for preferred printings. -}; - -#endif // COCKATRICE_CARD_DATABASE_QUERIER_H diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp deleted file mode 100644 index f7e6bbfcf..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "card_database_parser.h" - -#include - -SetNameMap ICardDatabaseParser::sets; - -ICardDatabaseParser::ICardDatabaseParser(ICardSetPriorityController *_cardSetPriorityController) - : cardSetPriorityController(_cardSetPriorityController) -{ -} -void ICardDatabaseParser::clearSetlist() -{ - sets.clear(); -} - -CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName, - const QString &longName, - const QString &setType, - const QDate &releaseDate, - const CardSet::Priority priority) -{ - if (sets.contains(setName)) { - return sets.value(setName); - } - - CardSetPtr newSet = CardSet::newInstance(cardSetPriorityController, setName); - newSet->setLongName(longName); - newSet->setSetType(setType); - newSet->setReleaseDate(releaseDate); - newSet->setPriority(priority); - - sets.insert(setName, newSet); - emit addSet(newSet); - return newSet; -} diff --git a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h b/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h deleted file mode 100644 index a8eceab5a..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/card_database_parser.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef CARDDATABASE_PARSER_H -#define CARDDATABASE_PARSER_H - -#include "../../card_info.h" - -#include -#include - -#define COCKATRICE_XML_XSI_NAMESPACE "http://www.w3.org/2001/XMLSchema-instance" - -/** - * @class ICardDatabaseParser - * @ingroup CardDatabase - * @brief Defines the base parser interface (ICardDatabaseParser) for all card database parsers. - * - * Provides methods for checking file compatibility, parsing, and saving card databases. - * Also provides shared access to the global set list for cross-referencing. - */ -class ICardDatabaseParser : public QObject -{ - Q_OBJECT -public: - ICardDatabaseParser(ICardSetPriorityController *cardSetPriorityController); - ~ICardDatabaseParser() override = default; - - /** - * @brief Checks whether this parser can parse the given file. - * @param name File name (used for extension checks). - * @param device QIODevice representing the file content. - * @return true if the parser can handle this file. - */ - virtual bool getCanParseFile(const QString &name, QIODevice &device) = 0; - - /** - * @brief Parses a database file and emits addCard/addSet signals. - * @param device QIODevice representing the file content. - */ - virtual void parseFile(QIODevice &device) = 0; - - /** - * @brief Saves card and set data to a file. - * @param _formats - * @param sets Map of sets to save. - * @param cards Map of cards to save. - * @param fileName Target file path. - * @param sourceUrl Optional source URL of the database. - * @param sourceVersion Optional version string of the source. - * @return true if save succeeded. - */ - virtual bool saveToFile(FormatRulesNameMap _formats, - SetNameMap sets, - CardNameMap cards, - const QString &fileName, - const QString &sourceUrl = "unknown", - const QString &sourceVersion = "unknown") = 0; - - /** @brief Clears the cached global set list. */ - static void clearSetlist(); - -protected: - /** @brief Cached global list of sets shared between all parsers. */ - static SetNameMap sets; - ICardSetPriorityController *cardSetPriorityController; - - /** - * @brief Internal helper to add a set to the global set cache. - * @param setName Short set name. - * @param longName Optional full name. - * @param setType Optional set type string. - * @param releaseDate Optional release date. - * @param priority Optional priority (fallback if not specified). - * @return Pointer to the added or existing CardSet instance. - */ - CardSetPtr internalAddSet(const QString &setName, - const QString &longName = "", - const QString &setType = "", - const QDate &releaseDate = QDate(), - const CardSet::Priority priority = CardSet::PriorityFallback); - -signals: - /** Emitted when a card is loaded from the database. */ - void addCard(CardInfoPtr card); - - /** Emitted when a set is loaded from the database. */ - void addSet(CardSetPtr set); - - void addFormat(FormatRulesPtr format); -}; - -Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser") - -#endif diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp deleted file mode 100644 index ba27d63c4..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.cpp +++ /dev/null @@ -1,496 +0,0 @@ -#include "cockatrice_xml_3.h" - -#include "../../relation/card_relation.h" -#include "../../relation/card_relation_type.h" - -#include -#include -#include -#include -#include - -#define COCKATRICE_XML3_TAGNAME "cockatrice_carddatabase" -#define COCKATRICE_XML3_TAGVER 3 -#define COCKATRICE_XML3_SCHEMALOCATION \ - "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v3/cards.xsd" - -CockatriceXml3Parser::CockatriceXml3Parser(ICardSetPriorityController *_cardSetPriorityController) - : ICardDatabaseParser(_cardSetPriorityController) -{ -} - -bool CockatriceXml3Parser::getCanParseFile(const QString &fileName, QIODevice &device) -{ - qCInfo(CockatriceXml3Log) << "Trying to parse: " << fileName; - - if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) { - qCInfo(CockatriceXml3Log) << "Parsing failed: wrong extension"; - return false; - } - - QXmlStreamReader xml(&device); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::StartElement) { - if (xml.name().toString() == COCKATRICE_XML3_TAGNAME) { - int version = xml.attributes().value("version").toString().toInt(); - if (version == COCKATRICE_XML3_TAGVER) { - return true; - } else { - qCInfo(CockatriceXml3Log) << "Parsing failed: wrong version" << version; - return false; - } - - } else { - qCInfo(CockatriceXml3Log) << "Parsing failed: wrong element tag" << xml.name(); - return false; - } - } - } - - return true; -} - -void CockatriceXml3Parser::parseFile(QIODevice &device) -{ - QXmlStreamReader xml(&device); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::StartElement) { - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto name = xml.name().toString(); - if (name == "sets") { - loadSetsFromXml(xml); - } else if (name == "cards") { - loadCardsFromXml(xml); - } else if (!name.isEmpty()) { - qCInfo(CockatriceXml3Log) << "Unknown item" << name << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - } - } - - if (xml.hasError()) { - QString preamble = tr("Parse error at line %1 col %2:").arg(xml.lineNumber()).arg(xml.columnNumber()); - qCWarning(CockatriceXml3Log).noquote() << preamble << xml.errorString(); - } -} - -void CockatriceXml3Parser::loadSetsFromXml(QXmlStreamReader &xml) -{ - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto name = xml.name().toString(); - if (name == "set") { - QString shortName, longName, setType; - QDate releaseDate; - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - name = xml.name().toString(); - - if (name == "name") { - shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (name == "longname") { - longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (name == "settype") { - setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (name == "releasedate") { - releaseDate = - QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); - } else if (!name.isEmpty()) { - qCInfo(CockatriceXml3Log) << "Unknown set property" << name << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - - internalAddSet(shortName, longName, setType, releaseDate); - } - } -} - -QString CockatriceXml3Parser::getMainCardType(QString &type) -{ - QString result = type; - /* - Legendary Artifact Creature - Golem - Instant // Instant - */ - - int pos; - if ((pos = result.indexOf('-')) != -1) { - result.remove(pos, result.length()); - } - - if ((pos = result.indexOf("—")) != -1) { - result.remove(pos, result.length()); - } - - if ((pos = result.indexOf("//")) != -1) { - result.remove(pos, result.length()); - } - - result = result.simplified(); - /* - Legendary Artifact Creature - Instant - */ - - if ((pos = result.lastIndexOf(' ')) != -1) { - result = result.mid(pos + 1); - } - /* - Creature - Instant - */ - - return result; -} - -void CockatriceXml3Parser::loadCardsFromXml(QXmlStreamReader &xml) -{ - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto xmlName = xml.name().toString(); - if (xmlName == "card") { - QString name = QString(""); - QString text = QString(""); - QVariantHash properties = QVariantHash(); - QString colors = QString(""); - QList relatedCards, reverseRelatedCards; - auto _sets = SetToPrintingsMap(); - int tableRow = 0; - bool cipt = false; - bool landscapeOrientation = false; - bool isToken = false; - bool upsideDown = false; - - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - xmlName = xml.name().toString(); - - // variable - assigned properties - if (xmlName == "name") { - name = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "text") { - text = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "color" || xmlName == "colors") { - colors.append(xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xmlName == "token") { - isToken = static_cast(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); - // generic properties - } else if (xmlName == "manacost") { - properties.insert("manacost", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xmlName == "cmc") { - properties.insert("cmc", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xmlName == "type") { - QString type = xml.readElementText(QXmlStreamReader::IncludeChildElements); - properties.insert("type", type); - properties.insert("maintype", getMainCardType(type)); - } else if (xmlName == "pt") { - properties.insert("pt", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } else if (xmlName == "loyalty") { - properties.insert("loyalty", xml.readElementText(QXmlStreamReader::IncludeChildElements)); - // positioning info - } else if (xmlName == "tablerow") { - tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); - } else if (xmlName == "cipt") { - cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xmlName == "landscapeOrientation") { - landscapeOrientation = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xmlName == "upsidedown") { - upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - // sets - } else if (xmlName == "set") { - // NOTE: attributes must be read before readElementText() - QXmlStreamAttributes attrs = xml.attributes(); - QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - PrintingInfo setInfo(internalAddSet(setName)); - if (attrs.hasAttribute("muId")) { - setInfo.setProperty("muid", attrs.value("muId").toString()); - } - - if (attrs.hasAttribute("muId")) { - setInfo.setProperty("uuid", attrs.value("uuId").toString()); - } - - if (attrs.hasAttribute("picURL")) { - setInfo.setProperty("picurl", attrs.value("picURL").toString()); - } - - if (attrs.hasAttribute("num")) { - setInfo.setProperty("num", attrs.value("num").toString()); - } - - if (attrs.hasAttribute("rarity")) { - setInfo.setProperty("rarity", attrs.value("rarity").toString()); - } - _sets[setName].append(setInfo); - // related cards - } else if (xmlName == "related" || xmlName == "reverse-related") { - CardRelationType attach = CardRelationType::DoesNotAttach; - bool exclude = false; - bool variable = false; - int count = 1; - QXmlStreamAttributes attrs = xml.attributes(); - QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - if (attrs.hasAttribute("count")) { - if (attrs.value("count").toString().indexOf("x=") == 0) { - variable = true; - count = attrs.value("count").toString().remove(0, 2).toInt(); - } else if (attrs.value("count").toString().indexOf("x") == 0) { - variable = true; - } else { - count = attrs.value("count").toString().toInt(); - } - - if (count < 1) { - count = 1; - } - } - - if (attrs.hasAttribute("attach")) { - attach = CardRelationType::AttachTo; - } - - if (attrs.hasAttribute("exclude")) { - exclude = true; - } - - auto *relation = new CardRelation(cardName, attach, exclude, variable, count); - if (xmlName == "reverse-related") { - reverseRelatedCards << relation; - } else { - relatedCards << relation; - } - } else if (!xmlName.isEmpty()) { - qCInfo(CockatriceXml3Log) << "Unknown card property" << xmlName << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - - if (name.isEmpty()) { - qCWarning(CockatriceXml3Log) << "Encountered card with empty name; skipping"; - continue; - } - - properties.insert("colors", colors); - - CardInfo::UiAttributes attributes = {.cipt = cipt, - .landscapeOrientation = landscapeOrientation, - .tableRow = tableRow, - .upsideDownArt = upsideDown}; - CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, - reverseRelatedCards, _sets, attributes); - emit addCard(newCard); - } - } -} - -static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set) -{ - if (set.isNull()) { - qCWarning(CockatriceXml3Log) << "&operator<< set is nullptr"; - return xml; - } - - xml.writeStartElement("set"); - xml.writeTextElement("name", set->getShortName()); - xml.writeTextElement("longname", set->getLongName()); - xml.writeTextElement("settype", set->getSetType()); - xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate)); - xml.writeEndElement(); - - return xml; -} - -static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info) -{ - if (info.isNull()) { - qCWarning(CockatriceXml3Log) << "operator<< info is nullptr"; - return xml; - } - - QString tmpString; - - xml.writeStartElement("card"); - - // variable - assigned properties - xml.writeTextElement("name", info->getName()); - xml.writeTextElement("text", info->getText()); - if (info->getIsToken()) { - xml.writeTextElement("token", "1"); - } - - // generic properties - xml.writeTextElement("manacost", info->getProperty("manacost")); - xml.writeTextElement("cmc", info->getProperty("cmc")); - xml.writeTextElement("type", info->getProperty("type")); - - int colorSize = info->getColors().size(); - for (int i = 0; i < colorSize; ++i) { - xml.writeTextElement("color", info->getColors().at(i)); - } - - tmpString = info->getProperty("pt"); - if (!tmpString.isEmpty()) { - xml.writeTextElement("pt", tmpString); - } - - tmpString = info->getProperty("loyalty"); - if (!tmpString.isEmpty()) { - xml.writeTextElement("loyalty", tmpString); - } - - // sets - const SetToPrintingsMap setMap = info->getSets(); - for (const auto &printings : setMap) { - for (const PrintingInfo &set : printings) { - xml.writeStartElement("set"); - xml.writeAttribute("rarity", set.getProperty("rarity")); - xml.writeAttribute("muId", set.getProperty("muid")); - xml.writeAttribute("uuId", set.getProperty("uuid")); - - tmpString = set.getProperty("num"); - if (!tmpString.isEmpty()) { - xml.writeAttribute("num", tmpString); - } - - tmpString = set.getProperty("picurl"); - if (!tmpString.isEmpty()) { - xml.writeAttribute("picURL", tmpString); - } - - xml.writeCharacters(set.getSet()->getShortName()); - xml.writeEndElement(); - } - } - - // related cards - const QList related = info->getRelatedCards(); - for (auto i : related) { - xml.writeStartElement("related"); - if (i->getDoesAttach()) { - xml.writeAttribute("attach", "attach"); - } - if (i->getIsCreateAllExclusion()) { - xml.writeAttribute("exclude", "exclude"); - } - - if (i->getIsVariable()) { - if (1 == i->getDefaultCount()) { - xml.writeAttribute("count", "x"); - } else { - xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); - } - } else if (1 != i->getDefaultCount()) { - xml.writeAttribute("count", QString::number(i->getDefaultCount())); - } - xml.writeCharacters(i->getName()); - xml.writeEndElement(); - } - const QList reverseRelated = info->getReverseRelatedCards(); - for (auto i : reverseRelated) { - xml.writeStartElement("reverse-related"); - if (i->getDoesAttach()) { - xml.writeAttribute("attach", "attach"); - } - - if (i->getIsCreateAllExclusion()) { - xml.writeAttribute("exclude", "exclude"); - } - - if (i->getIsVariable()) { - if (1 == i->getDefaultCount()) { - xml.writeAttribute("count", "x"); - } else { - xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); - } - } else if (1 != i->getDefaultCount()) { - xml.writeAttribute("count", QString::number(i->getDefaultCount())); - } - xml.writeCharacters(i->getName()); - xml.writeEndElement(); - } - - // positioning - const CardInfo::UiAttributes &attributes = info->getUiAttributes(); - xml.writeTextElement("tablerow", QString::number(attributes.tableRow)); - if (attributes.cipt) { - xml.writeTextElement("cipt", "1"); - } - if (attributes.landscapeOrientation) { - xml.writeTextElement("landscapeOrientation", "1"); - } - if (attributes.upsideDownArt) { - xml.writeTextElement("upsidedown", "1"); - } - - xml.writeEndElement(); // card - - return xml; -} - -bool CockatriceXml3Parser::saveToFile(FormatRulesNameMap _formats, - SetNameMap _sets, - CardNameMap cards, - const QString &fileName, - const QString &sourceUrl, - const QString &sourceVersion) -{ - Q_UNUSED(_formats); - - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - return false; - } - - QXmlStreamWriter xml(&file); - - xml.setAutoFormatting(true); - xml.writeStartDocument(); - xml.writeStartElement(COCKATRICE_XML3_TAGNAME); - xml.writeAttribute("version", QString::number(COCKATRICE_XML3_TAGVER)); - xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE); - xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML3_SCHEMALOCATION); - - xml.writeStartElement("info"); - xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING)); - xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); - xml.writeTextElement("sourceUrl", sourceUrl); - xml.writeTextElement("sourceVersion", sourceVersion); - xml.writeEndElement(); - - if (_sets.count() > 0) { - xml.writeStartElement("sets"); - for (CardSetPtr set : _sets) { - xml << set; - } - xml.writeEndElement(); - } - - if (cards.count() > 0) { - xml.writeStartElement("cards"); - for (CardInfoPtr card : cards) { - xml << card; - } - xml.writeEndElement(); - } - - xml.writeEndElement(); // cockatrice_carddatabase - xml.writeEndDocument(); - - return true; -} diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h deleted file mode 100644 index a01b705aa..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_3.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef COCKATRICE_XML3_H -#define COCKATRICE_XML3_H - -#include "card_database_parser.h" - -#include -#include - -inline Q_LOGGING_CATEGORY(CockatriceXml3Log, "cockatrice_xml.xml_3_parser"); - -/** - * @class CockatriceXml3Parser - * @ingroup CardDatabase - * @brief Parses version 3 of the Cockatrice XML Schema. - * - * This parser reads a Cockatrice XML3 database and emits CardInfoPtr - * and CardSetPtr objects. All card properties are read individually. - * - * @note Differences from v4: - * - No block; properties are hardcoded (manacost, cmc, type, pt, loyalty, etc.). - * - No set priority field. - * - No support for rebalanced cards or preferences. - * - Related cards support only attach, exclude, variable, and count attributes. - */ -class CockatriceXml3Parser : public ICardDatabaseParser -{ - Q_OBJECT -public: - CockatriceXml3Parser(ICardSetPriorityController *cardSetPriorityController); - ~CockatriceXml3Parser() override = default; - - /** - * @brief Determines if the parser can handle this file. - * @param name File name. - * @param device Open QIODevice containing the XML. - * @return True if the file is a Cockatrice XML3 database. - */ - bool getCanParseFile(const QString &name, QIODevice &device) override; - - /** - * @brief Parse the XML database. - * @param device Open QIODevice positioned at start of file. - */ - void parseFile(QIODevice &device) override; - - /** - * @brief Save sets and cards back to an XML3 file. - */ - bool saveToFile(FormatRulesNameMap _formats, - SetNameMap _sets, - CardNameMap cards, - const QString &fileName, - const QString &sourceUrl = "unknown", - const QString &sourceVersion = "unknown") override; - -private: - /** - * @brief Load all elements from the XML stream. - * @param xml The open QXmlStreamReader positioned at the element. - * Parses each node and emits addCard signals for each CardInfoPtr created. - */ - void loadCardsFromXml(QXmlStreamReader &xml); - - /** - * @brief Load all elements from the XML stream. - * @param xml The open QXmlStreamReader positioned at the element. - * Parses each node and adds them to the shared set cache. - */ - void loadSetsFromXml(QXmlStreamReader &xml); - - /** - * @brief Extracts the main card type from a full type string. - * @param type The full type string (e.g., "Legendary Artifact Creature - Golem") - * @return The primary type (e.g., "Creature"). - */ - QString getMainCardType(QString &type); -}; - -#endif \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp deleted file mode 100644 index cc0220526..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.cpp +++ /dev/null @@ -1,625 +0,0 @@ -#include "cockatrice_xml_4.h" - -#include "../../relation/card_relation.h" - -#include -#include -#include -#include -#include -#include - -#define COCKATRICE_XML4_TAGNAME "cockatrice_carddatabase" -#define COCKATRICE_XML4_TAGVER 4 -#define COCKATRICE_XML4_SCHEMALOCATION \ - "https://raw.githubusercontent.com/Cockatrice/Cockatrice/master/doc/carddatabase_v4/cards.xsd" - -CockatriceXml4Parser::CockatriceXml4Parser(ICardPreferenceProvider *_cardPreferenceProvider, - ICardSetPriorityController *_cardSetPriorityController) - : ICardDatabaseParser(_cardSetPriorityController), cardPreferenceProvider(_cardPreferenceProvider) -{ -} - -bool CockatriceXml4Parser::getCanParseFile(const QString &fileName, QIODevice &device) -{ - qCInfo(CockatriceXml4Log) << "Trying to parse: " << fileName; - - if (!fileName.endsWith(".xml", Qt::CaseInsensitive)) { - qCInfo(CockatriceXml4Log) << "Parsing failed: wrong extension"; - return false; - } - - QXmlStreamReader xml(&device); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::StartElement) { - if (xml.name().toString() == COCKATRICE_XML4_TAGNAME) { - int version = xml.attributes().value("version").toString().toInt(); - if (version == COCKATRICE_XML4_TAGVER) { - return true; - } else { - qCInfo(CockatriceXml4Log) << "Parsing failed: wrong version" << version; - return false; - } - - } else { - qCInfo(CockatriceXml4Log) << "Parsing failed: wrong element tag" << xml.name(); - return false; - } - } - } - - return true; -} - -void CockatriceXml4Parser::parseFile(QIODevice &device) -{ - QXmlStreamReader xml(&device); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::StartElement) { - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto xmlName = xml.name().toString(); - if (xmlName == "formats") { - loadFormats(xml); - } else if (xmlName == "sets") { - loadSetsFromXml(xml); - } else if (xmlName == "cards") { - loadCardsFromXml(xml); - } else if (!xmlName.isEmpty()) { - qCInfo(CockatriceXml4Log) << "Unknown item" << xmlName << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - } - } - - if (xml.hasError()) { - QString preamble = tr("Parse error at line %1 col %2:").arg(xml.lineNumber()).arg(xml.columnNumber()); - qCWarning(CockatriceXml4Log).noquote() << preamble << xml.errorString(); - } -} - -static QSharedPointer parseFormat(QXmlStreamReader &xml) -{ - auto rulesPtr = FormatRulesPtr(new FormatRules()); - - if (xml.attributes().hasAttribute("formatName")) { - rulesPtr->formatName = xml.attributes().value("formatName").toString(); - } - - while (!xml.atEnd()) { - auto token = xml.readNext(); - - if (token == QXmlStreamReader::EndElement && xml.name().toString() == "format") { - break; - } - - if (token != QXmlStreamReader::StartElement) { - continue; - } - - QString xmlName = xml.name().toString(); - - if (xmlName == "minDeckSize") { - rulesPtr->minDeckSize = xml.readElementText().toInt(); - } else if (xmlName == "maxDeckSize") { - QString text = xml.readElementText(); - rulesPtr->maxDeckSize = text.toInt(); - } else if (xmlName == "maxSideboardSize") { - rulesPtr->maxSideboardSize = xml.readElementText().toInt(); - } else if (xmlName == "allowedCounts") { - while (!xml.atEnd()) { - token = xml.readNext(); - - if (token == QXmlStreamReader::EndElement && xml.name().toString() == "allowedCounts") { - break; - } - - if (token == QXmlStreamReader::StartElement && xml.name().toString() == "count") { - - AllowedCount c; - - QString maxAttr = xml.attributes().value("max").toString(); - c.max = (maxAttr == "unlimited") ? -1 : maxAttr.toInt(); - - c.label = xml.readElementText().trimmed(); - - rulesPtr->allowedCounts.append(c); - } - } - } else if (xmlName == "exceptions") { - while (!xml.atEnd()) { - token = xml.readNext(); - - if (token == QXmlStreamReader::EndElement && xml.name().toString() == "exceptions") { - break; - } - - if (token == QXmlStreamReader::StartElement && xml.name().toString() == "exception") { - ExceptionRule ex; - - while (!xml.atEnd()) { - token = xml.readNext(); - - if (token == QXmlStreamReader::EndElement && xml.name().toString() == "exception") { - break; - } - - if (token == QXmlStreamReader::StartElement) { - QString ename = xml.name().toString(); - - if (ename == "maxCopies") { - QString text = xml.readElementText(); - ex.maxCopies = (text == "unlimited") ? -1 : text.toInt(); - } else if (ename == "cardCondition") { - CardCondition cond; - cond.field = xml.attributes().value("field").toString(); - cond.matchType = xml.attributes().value("match").toString(); - cond.value = xml.attributes().value("value").toString(); - ex.conditions.append(cond); - xml.skipCurrentElement(); - } else { - xml.skipCurrentElement(); - } - } - } - - rulesPtr->exceptions.append(ex); - } - } - } else { - xml.skipCurrentElement(); - } - } - - return rulesPtr; -} - -void CockatriceXml4Parser::loadFormats(QXmlStreamReader &xml) -{ - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - if (xml.name().toString() == "format") { - auto rulesPtr = parseFormat(xml); - emit addFormat(rulesPtr); - } - } -} - -void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml) -{ - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto xmlName = xml.name().toString(); - if (xmlName == "set") { - QString shortName, longName, setType; - QDate releaseDate; - short priority; - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - xmlName = xml.name().toString(); - - if (xmlName == "name") { - shortName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "longname") { - longName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "settype") { - setType = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "releasedate") { - releaseDate = - QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate); - } else if (xmlName == "priority") { - priority = xml.readElementText(QXmlStreamReader::IncludeChildElements).toShort(); - } else if (!xmlName.isEmpty()) { - qCInfo(CockatriceXml4Log) << "Unknown set property" << xmlName << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - - internalAddSet(shortName, longName, setType, releaseDate, static_cast(priority)); - } - } -} - -QVariantHash CockatriceXml4Parser::loadCardPropertiesFromXml(QXmlStreamReader &xml) -{ - QVariantHash properties = QVariantHash(); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto xmlName = xml.name().toString(); - if (!xmlName.isEmpty()) { - properties.insert(xmlName, xml.readElementText(QXmlStreamReader::IncludeChildElements)); - } - } - return properties; -} - -void CockatriceXml4Parser::loadCardsFromXml(QXmlStreamReader &xml) -{ - bool includeRebalancedCards = cardPreferenceProvider->getIncludeRebalancedCards(); - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - auto xmlName = xml.name().toString(); - - if (xmlName == "card") { - QString name = QString(""); - QString text = QString(""); - QVariantHash properties = QVariantHash(); - QList relatedCards, reverseRelatedCards; - auto _sets = SetToPrintingsMap(); - int tableRow = 0; - bool cipt = false; - bool landscapeOrientation = false; - bool isToken = false; - bool upsideDown = false; - - while (!xml.atEnd()) { - if (xml.readNext() == QXmlStreamReader::EndElement) { - break; - } - - xmlName = xml.name().toString(); - - // variable - assigned properties - if (xmlName == "name") { - name = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "text") { - text = xml.readElementText(QXmlStreamReader::IncludeChildElements); - } else if (xmlName == "token") { - isToken = static_cast(xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt()); - // generic properties - } else if (xmlName == "prop") { - properties = loadCardPropertiesFromXml(xml); - // positioning info - } else if (xmlName == "tablerow") { - tableRow = xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt(); - } else if (xmlName == "cipt") { - cipt = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xmlName == "landscapeOrientation") { - landscapeOrientation = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - } else if (xmlName == "upsidedown") { - upsideDown = (xml.readElementText(QXmlStreamReader::IncludeChildElements) == "1"); - // sets - } else if (xmlName == "set") { - // NOTE: attributes but be read before readElementText() - QXmlStreamAttributes attrs = xml.attributes(); - QString setName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - auto set = internalAddSet(setName); - if (set->getEnabled()) { - PrintingInfo printingInfo(set); - for (QXmlStreamAttribute attr : attrs) { - QString attrName = attr.name().toString(); - if (attrName == "picURL") - attrName = "picurl"; - printingInfo.setProperty(attrName, attr.value().toString()); - } - - // This is very much a hack and not the right place to - // put this check, as it requires a reload of Cockatrice - // to be apply. - // - // However, this is also true of the `set->getEnabled()` - // check above (which is currently bugged as well), so - // we'll fix both at the same time. - if (includeRebalancedCards || printingInfo.getProperty("isRebalanced") != "true") { - _sets[setName].append(printingInfo); - } - } - // related cards - } else if (xmlName == "related" || xmlName == "reverse-related") { - CardRelationType attachType = CardRelationType::DoesNotAttach; - bool exclude = false; - bool variable = false; - bool persistent = false; - int count = 1; - QXmlStreamAttributes attrs = xml.attributes(); - QString cardName = xml.readElementText(QXmlStreamReader::IncludeChildElements); - if (attrs.hasAttribute("count")) { - if (attrs.value("count").toString().indexOf("x=") == 0) { - variable = true; - count = attrs.value("count").toString().remove(0, 2).toInt(); - } else if (attrs.value("count").toString().indexOf("x") == 0) { - variable = true; - } else { - count = attrs.value("count").toString().toInt(); - } - - if (count < 1) { - count = 1; - } - } - - if (attrs.hasAttribute("attach")) { - attachType = attrs.value("attach").toString() == "transform" ? CardRelationType::TransformInto - : CardRelationType::AttachTo; - } - - if (attrs.hasAttribute("exclude")) { - exclude = true; - } - - if (attrs.hasAttribute("persistent")) { - persistent = true; - } - - auto *relation = new CardRelation(cardName, attachType, exclude, variable, count, persistent); - if (xmlName == "reverse-related") { - reverseRelatedCards << relation; - } else { - relatedCards << relation; - } - } else if (!xmlName.isEmpty()) { - qCInfo(CockatriceXml4Log) << "Unknown card property" << xmlName << ", trying to continue anyway"; - xml.skipCurrentElement(); - } - } - - if (name.isEmpty()) { - qCWarning(CockatriceXml4Log) << "Encountered card with empty name; skipping"; - continue; - } - - CardInfo::UiAttributes attributes = {.cipt = cipt, - .landscapeOrientation = landscapeOrientation, - .tableRow = tableRow, - .upsideDownArt = upsideDown}; - CardInfoPtr newCard = CardInfo::newInstance(name, text, isToken, properties, relatedCards, - reverseRelatedCards, _sets, attributes); - emit addCard(newCard); - } - } -} - -static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const QSharedPointer &rulesPtr) -{ - if (rulesPtr.isNull()) { - qCWarning(CockatriceXml4Log) << "&operator<< FormatRules is nullptr"; - return xml; - } - - const FormatRules &rules = *rulesPtr; - - xml.writeStartElement("format"); - if (!rules.formatName.isEmpty()) { - xml.writeAttribute("formatName", rules.formatName); - } - - xml.writeTextElement("minDeckSize", QString::number(rules.minDeckSize)); - xml.writeTextElement("maxDeckSize", rules.maxDeckSize >= 0 ? QString::number(rules.maxDeckSize) : "0"); - xml.writeTextElement("maxSideboardSize", QString::number(rules.maxSideboardSize)); - if (!rules.allowedCounts.isEmpty()) { - xml.writeStartElement("allowedCounts"); - - for (const AllowedCount &c : rules.allowedCounts) { - xml.writeStartElement("count"); - xml.writeAttribute("max", c.max == -1 ? "unlimited" : QString::number(c.max)); - xml.writeCharacters(c.label); - xml.writeEndElement(); // count - } - - xml.writeEndElement(); // allowedCounts - } - - if (!rules.exceptions.isEmpty()) { - xml.writeStartElement("exceptions"); - for (const ExceptionRule &ex : rules.exceptions) { - xml.writeStartElement("exception"); - xml.writeTextElement("maxCopies", ex.maxCopies == -1 ? "unlimited" : QString::number(ex.maxCopies)); - - for (const CardCondition &cond : ex.conditions) { - xml.writeStartElement("cardCondition"); - xml.writeAttribute("field", cond.field); - xml.writeAttribute("match", cond.matchType); - xml.writeAttribute("value", cond.value); - xml.writeEndElement(); // cardCondition - } - - xml.writeEndElement(); // exception - } - xml.writeEndElement(); // exceptions - } - - xml.writeEndElement(); // format - return xml; -} - -static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set) -{ - if (set.isNull()) { - qCWarning(CockatriceXml4Log) << "&operator<< set is nullptr"; - return xml; - } - - xml.writeStartElement("set"); - xml.writeTextElement("name", set->getShortName()); - xml.writeTextElement("longname", set->getLongName()); - xml.writeTextElement("settype", set->getSetType()); - xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate)); - xml.writeTextElement("priority", QString::number(set->getPriority())); - xml.writeEndElement(); - - return xml; -} - -static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardInfoPtr &info) -{ - if (info.isNull()) { - qCWarning(CockatriceXml4Log) << "operator<< info is nullptr"; - return xml; - } - - QString tmpString; - - xml.writeStartElement("card"); - - // variable - assigned properties - xml.writeTextElement("name", info->getName()); - xml.writeTextElement("text", info->getText()); - if (info->getIsToken()) { - xml.writeTextElement("token", "1"); - } - - // generic properties - xml.writeStartElement("prop"); - for (QString propName : info->getProperties()) { - xml.writeTextElement(propName, info->getProperty(propName)); - } - xml.writeEndElement(); - - // sets - for (const auto &printings : info->getSets()) { - for (const PrintingInfo &set : printings) { - xml.writeStartElement("set"); - for (const QString &propName : set.getProperties()) { - xml.writeAttribute(propName, set.getProperty(propName)); - } - - xml.writeCharacters(set.getSet()->getShortName()); - xml.writeEndElement(); - } - } - - // related cards - const QList related = info->getRelatedCards(); - for (auto i : related) { - xml.writeStartElement("related"); - if (i->getDoesAttach()) { - xml.writeAttribute("attach", i->getAttachTypeAsString()); - } - if (i->getIsCreateAllExclusion()) { - xml.writeAttribute("exclude", "exclude"); - } - if (i->getIsPersistent()) { - xml.writeAttribute("persistent", "persistent"); - } - if (i->getIsVariable()) { - if (1 == i->getDefaultCount()) { - xml.writeAttribute("count", "x"); - } else { - xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); - } - } else if (1 != i->getDefaultCount()) { - xml.writeAttribute("count", QString::number(i->getDefaultCount())); - } - xml.writeCharacters(i->getName()); - xml.writeEndElement(); - } - const QList reverseRelated = info->getReverseRelatedCards(); - for (auto i : reverseRelated) { - xml.writeStartElement("reverse-related"); - if (i->getDoesAttach()) { - xml.writeAttribute("attach", i->getAttachTypeAsString()); - } - - if (i->getIsCreateAllExclusion()) { - xml.writeAttribute("exclude", "exclude"); - } - - if (i->getIsPersistent()) { - xml.writeAttribute("persistent", "persistent"); - } - if (i->getIsVariable()) { - if (1 == i->getDefaultCount()) { - xml.writeAttribute("count", "x"); - } else { - xml.writeAttribute("count", "x=" + QString::number(i->getDefaultCount())); - } - } else if (1 != i->getDefaultCount()) { - xml.writeAttribute("count", QString::number(i->getDefaultCount())); - } - xml.writeCharacters(i->getName()); - xml.writeEndElement(); - } - - // positioning - const CardInfo::UiAttributes &attributes = info->getUiAttributes(); - xml.writeTextElement("tablerow", QString::number(attributes.tableRow)); - if (attributes.cipt) { - xml.writeTextElement("cipt", "1"); - } - if (attributes.landscapeOrientation) { - xml.writeTextElement("landscapeOrientation", "1"); - } - if (attributes.upsideDownArt) { - xml.writeTextElement("upsidedown", "1"); - } - - xml.writeEndElement(); // card - - return xml; -} - -bool CockatriceXml4Parser::saveToFile(FormatRulesNameMap _formats, - SetNameMap _sets, - CardNameMap cards, - const QString &fileName, - const QString &sourceUrl, - const QString &sourceVersion) -{ - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - return false; - } - - QXmlStreamWriter xml(&file); - - xml.setAutoFormatting(true); - xml.writeStartDocument(); - xml.writeStartElement(COCKATRICE_XML4_TAGNAME); - xml.writeAttribute("version", QString::number(COCKATRICE_XML4_TAGVER)); - xml.writeAttribute("xmlns:xsi", COCKATRICE_XML_XSI_NAMESPACE); - xml.writeAttribute("xsi:schemaLocation", COCKATRICE_XML4_SCHEMALOCATION); - - xml.writeStartElement("info"); - xml.writeTextElement("author", QCoreApplication::applicationName() + QString(" %1").arg(VERSION_STRING)); - xml.writeTextElement("createdAt", QDateTime::currentDateTimeUtc().toString(Qt::ISODate)); - xml.writeTextElement("sourceUrl", sourceUrl); - xml.writeTextElement("sourceVersion", sourceVersion); - xml.writeEndElement(); - - if (_formats.count() > 0) { - xml.writeStartElement("formats"); - for (FormatRulesPtr format : _formats) { - xml << format; - } - xml.writeEndElement(); - } - - if (_sets.count() > 0) { - xml.writeStartElement("sets"); - for (CardSetPtr set : _sets) { - xml << set; - } - xml.writeEndElement(); - } - - if (cards.count() > 0) { - xml.writeStartElement("cards"); - for (CardInfoPtr card : cards) { - xml << card; - } - xml.writeEndElement(); - } - - xml.writeEndElement(); // cockatrice_carddatabase - xml.writeEndDocument(); - - return true; -} diff --git a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h b/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h deleted file mode 100644 index 189e79535..000000000 --- a/libcockatrice_card/libcockatrice/card/database/parser/cockatrice_xml_4.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef COCKATRICE_XML4_H -#define COCKATRICE_XML4_H - -#include "card_database_parser.h" - -#include -#include -#include - -inline Q_LOGGING_CATEGORY(CockatriceXml4Log, "cockatrice_xml.xml_4_parser"); - -/** - * @class CockatriceXml4Parser - * @ingroup CardDatabase - * @brief Parses version 4 of the Cockatrice XML Schema. - * - * This parser reads a Cockatrice XML4 database and emits CardInfoPtr - * and CardSetPtr objects. Card properties are read inside blocks, - * making the parser more extensible and schema-compliant. - * - * @note Differences from v3: - * - Card properties are stored in blocks as a QVariantHash. - * - Sets can include a element. - * - Supports user preferences via ICardPreferenceProvider (e.g., skipping rebalanced cards). - * - Related cards support persistent relations and multiple attach types (e.g., transform). - * - More robust serialization; easier to extend schema in the future. - */ -class CockatriceXml4Parser : public ICardDatabaseParser -{ - Q_OBJECT -public: - explicit CockatriceXml4Parser(ICardPreferenceProvider *cardPreferenceProvider, - ICardSetPriorityController *cardSetPriorityController); - ~CockatriceXml4Parser() override = default; - - /** - * @brief Determines if the parser can handle this file. - * @param name File name. - * @param device Open QIODevice containing the XML. - * @return True if the file is a Cockatrice XML4 database. - */ - bool getCanParseFile(const QString &name, QIODevice &device) override; - - /** - * @brief Parse the XML database. - * @param device Open QIODevice positioned at start of file. - */ - void parseFile(QIODevice &device) override; - - /** - * @brief Save sets and cards back to an XML4 file. - */ - bool saveToFile(FormatRulesNameMap _formats, - SetNameMap _sets, - CardNameMap cards, - const QString &fileName, - const QString &sourceUrl = "unknown", - const QString &sourceVersion = "unknown") override; - -private: - ICardPreferenceProvider *cardPreferenceProvider; ///< Interface to handle user preferences - - /** - * @brief Loads a generic block from a element. - * @param xml The open QXmlStreamReader positioned at a element. - * @return A QVariantHash mapping property names to values. - */ - QVariantHash loadCardPropertiesFromXml(QXmlStreamReader &xml); - - /** - * @brief Load all elements from the XML stream. - * @param xml The open QXmlStreamReader positioned at the element. - * Honors the user's preference regarding rebalanced cards. - */ - void loadCardsFromXml(QXmlStreamReader &xml); - - void loadFormats(QXmlStreamReader &xml); - /** - * @brief Load all elements from the XML stream. - * @param xml The open QXmlStreamReader positioned at the element. - * Parses nodes including priority information. - */ - void loadSetsFromXml(QXmlStreamReader &xml); -}; - -#endif \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/format/format_legality_rules.cpp b/libcockatrice_card/libcockatrice/card/format/format_legality_rules.cpp deleted file mode 100644 index a6b3bb038..000000000 --- a/libcockatrice_card/libcockatrice/card/format/format_legality_rules.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "format_legality_rules.h" - -#include - -bool cardMatchesCondition(const CardInfo &card, const CardCondition &cond) -{ - CardMatchType type = matchTypeFromString(cond.matchType); - QString fieldValue; - if (cond.field == "name") { - fieldValue = card.getName(); - } else if (cond.field == "text") { - fieldValue = card.getText(); - } else { - fieldValue = card.getProperty(cond.field); - } - - switch (type) { - case CardMatchType::Equals: - return fieldValue == cond.value; - case CardMatchType::NotEquals: - return fieldValue != cond.value; - case CardMatchType::Contains: - return fieldValue.contains(cond.value, Qt::CaseInsensitive); - case CardMatchType::NotContains: - return !fieldValue.contains(cond.value, Qt::CaseInsensitive); - case CardMatchType::Regex: { - QRegularExpression re(cond.value, QRegularExpression::CaseInsensitiveOption); - return re.match(fieldValue).hasMatch(); - } - default: - return false; - } -} - -bool exceptionAppliesToCard(const CardInfo &card, const ExceptionRule &rule) -{ - for (const CardCondition &cond : rule.conditions) { - if (!cardMatchesCondition(card, cond)) { - return false; // all conditions must match - } - } - return true; -} - -bool cardHasAnyException(const CardInfo &card, const FormatRules &format) -{ - for (const ExceptionRule &rule : format.exceptions) { - if (exceptionAppliesToCard(card, rule)) { - return true; - } - } - return false; -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/format/format_legality_rules.h b/libcockatrice_card/libcockatrice/card/format/format_legality_rules.h deleted file mode 100644 index 16f2359ab..000000000 --- a/libcockatrice_card/libcockatrice/card/format/format_legality_rules.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef COCKATRICE_FORMAT_LEGALITY_RULES_H -#define COCKATRICE_FORMAT_LEGALITY_RULES_H - -#include -#include -#include - -class CardInfo; -using CardInfoPtr = QSharedPointer; - -struct CardCondition -{ - QString field; // e.g. "type", "maintype", "text" - QString matchType; // "contains", "equals", "regex", "notContains", etc. - QString value; // e.g. "Basic Land" -}; - -struct AllowedCount -{ - int max = 0; // 4, 1, 0, or -1 for unlimited - QString label; // "legal", "restricted", "banned" -}; - -struct ExceptionRule -{ - QList conditions; // All must match - int maxCopies = -1; // -1 = unlimited -}; - -struct FormatRules -{ - QString formatName; - int minDeckSize = 60; - int maxDeckSize = -1; // -1 = unlimited - int maxSideboardSize = 15; - - QList allowedCounts; - - QList exceptions; // Cards allowed to break maxCopies -}; - -enum class CardMatchType -{ - Equals, - NotEquals, - Contains, - NotContains, - Regex -}; - -// convert string to enum -inline CardMatchType matchTypeFromString(const QString &str) -{ - if (str == "equals") - return CardMatchType::Equals; - if (str == "notEquals") - return CardMatchType::NotEquals; - if (str == "contains") - return CardMatchType::Contains; - if (str == "notContains") - return CardMatchType::NotContains; - if (str == "regex") - return CardMatchType::Regex; - return CardMatchType::Equals; // fallback default -} - -bool cardMatchesCondition(const CardInfo &card, const CardCondition &cond); - -bool exceptionAppliesToCard(const CardInfo &card, const ExceptionRule &rule); - -bool cardHasAnyException(const CardInfo &card, const FormatRules &format); - -#endif // COCKATRICE_FORMAT_LEGALITY_RULES_H diff --git a/libcockatrice_card/libcockatrice/card/game_specific_terms.h b/libcockatrice_card/libcockatrice/card/game_specific_terms.h deleted file mode 100644 index 2931365ad..000000000 --- a/libcockatrice_card/libcockatrice/card/game_specific_terms.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file game_specific_terms.h - * @ingroup Cards - * @brief TODO: Document this. - */ - -#ifndef GAME_SPECIFIC_TERMS_H -#define GAME_SPECIFIC_TERMS_H - -#include -#include - -/* - * Collection of traslatable property names used in games, - * so we can use Game::Property instead of hardcoding strings. - * Note: Mtg = "Maybe that game" - */ - -namespace Mtg -{ -QString const CardType("type"); -QString const ConvertedManaCost("cmc"); -QString const Colors("colors"); -QString const Loyalty("loyalty"); -QString const MainCardType("maintype"); -QString const ManaCost("manacost"); -QString const PowTough("pt"); -QString const Side("side"); -QString const Layout("layout"); -QString const ColorIdentity("coloridentity"); - -inline static const QString getNicePropertyName(QString key) -{ - if (key == CardType) - return QCoreApplication::translate("Mtg", "Card Type"); - if (key == ConvertedManaCost) - return QCoreApplication::translate("Mtg", "Mana Value"); - if (key == Colors) - return QCoreApplication::translate("Mtg", "Color(s)"); - if (key == Loyalty) - return QCoreApplication::translate("Mtg", "Loyalty"); - if (key == MainCardType) - return QCoreApplication::translate("Mtg", "Main Card Type"); - if (key == ManaCost) - return QCoreApplication::translate("Mtg", "Mana Cost"); - if (key == PowTough) - return QCoreApplication::translate("Mtg", "P/T"); - if (key == Side) - return QCoreApplication::translate("Mtg", "Side"); - if (key == Layout) - return QCoreApplication::translate("Mtg", "Layout"); - if (key == ColorIdentity) - return QCoreApplication::translate("Mtg", "Color Identity"); - return key; -} -} // namespace Mtg - -#endif diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp deleted file mode 100644 index 91ebd6647..000000000 --- a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "card_name_normalizer.h" - -#include "../database/card_database_manager.h" -#include "../printing/exact_card.h" - -#include - -/** - * @brief Resolves the complete display name of a card. - * @param cardName Base name. - * @return Full display name, or the cardName unchanged if a display name is not found. - */ -static QString getCompleteCardName(const QString &cardName) -{ - ExactCard temp = CardDatabaseManager::query()->guessCard({cardName}); - if (temp) { - return temp.getName(); - } - - return cardName; -} - -QString CardNameNormalizer::operator()(const QString &cardNameString) const -{ - QString cardName = cardNameString; - - // Regex for advanced card parsing - static const QRegularExpression reSplitCard(R"( ?\/\/ ?)"); - static const QRegularExpression reBrace(R"( ?[\[\{][^\]\}]*[\]\}] ?)"); // not nested - static const QRegularExpression reRoundBrace(R"(^\([^\)]*\) ?)"); // () are only matched at start of string - static const QRegularExpression reDigitBrace(R"( ?\(\d*\) ?)"); // () are matched if containing digits - static const QRegularExpression reBraceDigit( - R"( ?\([\dA-Z]+\) *\d+$)"); // () are matched if containing setcode then a number - static const QRegularExpression reDoubleFacedMarker(R"( ?\(Transform\) ?)"); - - static const QHash differences{{QRegularExpression("’"), "'"}, - {QRegularExpression("Æ"), "Ae"}, - {QRegularExpression("æ"), "ae"}, - {QRegularExpression(" ?[|/]+ ?"), " // "}}; - - // Handle advanced card types - if (cardName.contains(reSplitCard)) { - cardName = cardName.split(reSplitCard).join(" // "); - } - - if (cardName.contains(reDoubleFacedMarker)) { - QStringList faces = cardName.split(reDoubleFacedMarker); - cardName = faces.first().trimmed(); - } - - // Remove unnecessary characters - cardName.remove(reBrace); - cardName.remove(reRoundBrace); // I'll be entirely honest here, these are split to accommodate just three cards - cardName.remove(reDigitBrace); // from un-sets that have a word in between round braces at the end - cardName.remove(reBraceDigit); // very specific format with the set code in () and collectors number after - - // Normalize characters - for (auto diff = differences.constBegin(); diff != differences.constEnd(); ++diff) { - cardName.replace(diff.key(), diff.value()); - } - - // Resolve complete card name - cardName = getCompleteCardName(cardName); - - return cardName; -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h b/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h deleted file mode 100644 index 716e7da80..000000000 --- a/libcockatrice_card/libcockatrice/card/import/card_name_normalizer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef COCKATRICE_CARD_NAME_NORMALIZER_H -#define COCKATRICE_CARD_NAME_NORMALIZER_H - -#include - -/** - * Functor that normalizes the raw card name parsed during a plaintext deck import into the card name that Cockatrice - * uses. - */ -struct CardNameNormalizer -{ - QString operator()(const QString &cardNameString) const; -}; - -#endif // COCKATRICE_CARD_NAME_NORMALIZER_H diff --git a/libcockatrice_card/libcockatrice/card/printing/exact_card.cpp b/libcockatrice_card/libcockatrice/card/printing/exact_card.cpp deleted file mode 100644 index 993b5b96e..000000000 --- a/libcockatrice_card/libcockatrice/card/printing/exact_card.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "exact_card.h" - -#include "../card_info.h" -#include "printing_info.h" - -/** - * Default constructor. - * This will set the CardInfoPtr to null. - * The printing will be the default-constructed PrintingInfo. - */ -ExactCard::ExactCard() -{ -} - -/** - * @param _card The card. Can be null. - * @param _printing The printing. Can be empty. - */ -ExactCard::ExactCard(const CardInfoPtr &_card, const PrintingInfo &_printing) : card(_card), printing(_printing) -{ -} - -bool ExactCard::operator==(const ExactCard &other) const -{ - return this->card == other.card && this->printing == other.printing; -} - -/** - * Convenience method to safely get the card's name. - * @return The name in the CardInfo, or an empty string if card is null - */ -QString ExactCard::getName() const -{ - return card.isNull() ? "" : card->getName(); -} - -/** - * Gets a view of the underlying cardInfoPtr. - * @return A const reference to the CardInfo, or an empty CardInfo if card is null - */ -const CardInfo &ExactCard::getInfo() const -{ - if (card.isNull()) { - static CardInfoPtr emptyCard = CardInfo::newInstance(""); - return *emptyCard; - } - return *card; -} - -/** - * The key used to identify this exact printing in the cache - */ -QString ExactCard::getPixmapCacheKey() const -{ - QString uuid = printing.getUuid(); - QString suffix = uuid.isEmpty() ? "" : "_" + uuid; - return QLatin1String("card_") + card->getName() + suffix; -} - -/** - * Checks if the card is null or empty. - */ -bool ExactCard::isEmpty() const -{ - return card.isNull() || card->getName().isEmpty(); -} - -/** - * Returns true if isEmpty() is false - */ -ExactCard::operator bool() const -{ - return !isEmpty(); -} - -/** - * Gets the CardInfo to emit the pixmapUpdated signal - */ -void ExactCard::emitPixmapUpdated() const -{ - emit card->pixmapUpdated(printing); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/printing/exact_card.h b/libcockatrice_card/libcockatrice/card/printing/exact_card.h deleted file mode 100644 index 74fc75da3..000000000 --- a/libcockatrice_card/libcockatrice/card/printing/exact_card.h +++ /dev/null @@ -1,119 +0,0 @@ -#ifndef EXACT_CARD_H -#define EXACT_CARD_H - -#include "../card_info.h" - -/** - * @class ExactCard - * @ingroup CardPrintings - * - * @brief Represents a specific card instance, defined by its CardInfo - * and a particular printing. - * - * An ExactCard identifies a card not only by its underlying CardInfoPtr - * (which may be null), but also by its PrintingInfo, which specifies the - * exact printing/variant. This allows distinguishing between different - * printings of the same logical card (e.g., different sets, promos, foils). - */ -class ExactCard -{ - CardInfoPtr card; - PrintingInfo printing; - -public: - /** - * @brief Constructs an empty ExactCard. - * - * The CardInfoPtr will be null, and PrintingInfo will be default-constructed. - * An empty ExactCard represents "no card". - */ - ExactCard(); - - /** - * @brief Constructs an ExactCard from a card and printing. - * - * @param _card The card info pointer. May be null. - * @param _printing The printing details. Defaults to an empty PrintingInfo. - */ - explicit ExactCard(const CardInfoPtr &_card, const PrintingInfo &_printing = PrintingInfo()); - - /** - * @brief Returns the underlying CardInfoPtr. - * - * May be null if the ExactCard is empty. - */ - [[nodiscard]] CardInfoPtr getCardPtr() const - { - return card; - } - - /** - * @brief Returns the printing information associated with this card. - * - * May be empty if no specific printing was assigned. - */ - [[nodiscard]] PrintingInfo getPrinting() const - { - return printing; - } - - /** - * @brief Compares both card pointer and printing for equality. - * - * Two ExactCard objects are equal only if both their CardInfoPtr and - * PrintingInfo values are equal. - */ - bool operator==(const ExactCard &other) const; - - /** - * @brief Convenience helper to get the card's display name. - * - * @return The card's name, or an empty string if the CardInfoPtr is null. - */ - [[nodiscard]] QString getName() const; - - /** - * @brief Returns a reference to the underlying CardInfo object. - * - * If the CardInfoPtr is null, returns a reference to a static empty CardInfo - * instance instead. This avoids null-dereferencing but means modifications - * to the returned object do not affect the ExactCard. - * - * @return A const reference to the CardInfo object. - */ - [[nodiscard]] const CardInfo &getInfo() const; - - /** - * @brief Generates a stable cache key for pixmap caching. - * - * The key includes the card's name and (if present) the printing UUID, - * allowing different printings of the same card to map to different cache entries. - */ - [[nodiscard]] QString getPixmapCacheKey() const; - - /** - * @brief Indicates whether this ExactCard represents no valid card. - * - * An ExactCard is considered empty if the CardInfoPtr is null or the - * card's name is empty. - */ - [[nodiscard]] bool isEmpty() const; - - /** - * @brief Boolean conversion indicating whether the card is valid (non-empty). - * - * @return true if not empty, false otherwise. - */ - explicit operator bool() const; - - /** - * @brief Emits the pixmapUpdated signal on the underlying CardInfo. - * - * Assumes CardInfoPtr is non-null. If called on an empty ExactCard, - * the behavior is undefined. - */ - void emitPixmapUpdated() const; -}; -Q_DECLARE_METATYPE(ExactCard) - -#endif // EXACT_CARD_H diff --git a/libcockatrice_card/libcockatrice/card/printing/printing_info.cpp b/libcockatrice_card/libcockatrice/card/printing/printing_info.cpp deleted file mode 100644 index 340998565..000000000 --- a/libcockatrice_card/libcockatrice/card/printing/printing_info.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "printing_info.h" - -#include "../set/card_set.h" - -PrintingInfo::PrintingInfo(const CardSetPtr &_set) : set(_set) -{ -} - -/** - * Gets the uuid property of the printing, or an empty string if the property isn't present - */ -QString PrintingInfo::getUuid() const -{ - return properties.value("uuid").toString(); -} - -QString PrintingInfo::getFlavorName() const -{ - return properties.value("flavorName").toString(); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/printing/printing_info.h b/libcockatrice_card/libcockatrice/card/printing/printing_info.h deleted file mode 100644 index ad7b33654..000000000 --- a/libcockatrice_card/libcockatrice/card/printing/printing_info.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef COCKATRICE_PRINTING_INFO_H -#define COCKATRICE_PRINTING_INFO_H - -#include "../set/card_set.h" - -#include -#include -#include - -class PrintingInfo; - -using SetToPrintingsMap = QMap>; - -/** - * @class PrintingInfo - * @ingroup CardPrintings - * - * @brief Represents metadata for a specific variation of a card within a set. - * - * A card can have multiple variations across sets. PrintingInfo associates - * a card with one such variation, and provides per-printing attributes - * such as identifiers or additional properties. - * - * Equality is defined as both the set and the property values being equal. - */ -class PrintingInfo -{ -public: - /** - * @brief Constructs a PrintingInfo associated with a specific set. - * - * @param _set The set this printing belongs to (defaults to null). - */ - explicit PrintingInfo(const CardSetPtr &_set = nullptr); - - /** - * @brief Destroys the PrintingInfo. - * - * Defaulted since no special cleanup is required. - */ - ~PrintingInfo() = default; - - /** - * @brief Equality operator. - * - * Two PrintingInfo objects are equal if they refer to the same set - * and contain the exact same property key/value pairs. - * - * @param other Another PrintingInfo to compare against. - * @return True if both set and properties are equal, otherwise false. - */ - bool operator==(const PrintingInfo &other) const - { - return this->set == other.set && this->properties == other.properties; - } - - /** - * @brief check if the info is empty, as if default constructed. - * - * @return True if both set and properties are empty, otherwise false. - */ - bool isEmpty() const - { - return set == nullptr && properties.isEmpty(); - } - -private: - CardSetPtr set; ///< The set this variation belongs to. - QVariantHash properties; ///< Key-value store for variation-specific attributes. - -public: - /** - * @brief Returns the set this printing belongs to. - * - * @return Pointer to the associated CardSet. - */ - [[nodiscard]] CardSetPtr getSet() const - { - return set; - } - - /** - * @brief Returns the list of property names defined for this printing. - * - * @return List of keys stored in the properties map. - */ - [[nodiscard]] QStringList getProperties() const - { - return properties.keys(); - } - - /** - * @brief Retrieves the value of a specific property. - * - * @param propertyName The key name of the property to query. - * @return The property value as a string, or an empty string if not set. - */ - [[nodiscard]] QString getProperty(const QString &propertyName) const - { - return properties.value(propertyName).toString(); - } - - /** - * @brief Sets or updates the value of a specific property. - * - * If the property already exists, its value is replaced. - * - * @param _name The name of the property. - * @param _value The string value to assign. - */ - void setProperty(const QString &_name, const QString &_value) - { - properties.insert(_name, _value); - } - - /** - * @brief Returns the providerID for this printing. - * - * @return A string representing the providerID. - */ - [[nodiscard]] QString getUuid() const; - - /** - * @brief Returns the flavorName for this printing. - * - * @return The flavorName, or empty if it isn't present. - */ - [[nodiscard]] QString getFlavorName() const; -}; - -#endif // COCKATRICE_PRINTING_INFO_H diff --git a/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp b/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp deleted file mode 100644 index 90e59e439..000000000 --- a/libcockatrice_card/libcockatrice/card/relation/card_relation.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "card_relation.h" - -#include "card_relation_type.h" - -CardRelation::CardRelation(const QString &_name, - CardRelationType _attachType, - bool _isCreateAllExclusion, - bool _isVariableCount, - int _defaultCount, - bool _isPersistent) - : name(_name), attachType(_attachType), isCreateAllExclusion(_isCreateAllExclusion), - isVariableCount(_isVariableCount), defaultCount(_defaultCount), isPersistent(_isPersistent) -{ -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/relation/card_relation.h b/libcockatrice_card/libcockatrice/card/relation/card_relation.h deleted file mode 100644 index 9ff704097..000000000 --- a/libcockatrice_card/libcockatrice/card/relation/card_relation.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef COCKATRICE_CARD_RELATION_H -#define COCKATRICE_CARD_RELATION_H - -#include "card_relation_type.h" - -#include -#include - -/** - * @class CardRelation - * @ingroup Cards - * - * @brief Represents a relationship between two cards. - * - * CardRelation objects define directional relationships, such as: - * - One card attaching to another. - * - One card transforming into another. - * - One card creating another instance. - * - * Relations may also define metadata such as whether multiple creations - * are possible, whether the relation is persistent, and default counts. - */ -class CardRelation : public QObject -{ - Q_OBJECT - -private: - QString name; ///< Name of the related card. - CardRelationType attachType; ///< Type of attachment. - bool isCreateAllExclusion; ///< True if this relation should exclude multiple creations in "create all" operations. - bool isVariableCount; ///< True if the number of creations is variable. - int defaultCount; ///< Default number of cards created or involved. - bool isPersistent; ///< True if this relation persists (i.e. is not destroyed) on zone change. - -public: - /** - * @brief Constructs a CardRelation with optional parameters. - * - * @param _name Name of the related card. - * @param _attachType Type of attachment. - * @param _isCreateAllExclusion Whether this relation excludes mass creation. - * @param _isVariableCount Whether the count is variable. - * @param _defaultCount Default number for creations or transformations. - * @param _isPersistent Whether the relation persists across zone changes. - */ - explicit CardRelation(const QString &_name = QString(), - CardRelationType _attachType = CardRelationType::DoesNotAttach, - bool _isCreateAllExclusion = false, - bool _isVariableCount = false, - int _defaultCount = 1, - bool _isPersistent = false); - - /** - * @brief Returns the name of the related card. - * - * @return Name as QString reference. - */ - [[nodiscard]] inline const QString &getName() const - { - return name; - } - - /** - * @brief Returns the type of attachment. - * - * @return Enum value representing the attachment type. - */ - [[nodiscard]] CardRelationType getAttachType() const - { - return attachType; - } - - /** - * @brief Returns true if the card is attached to another. - * - * @return True if attached, false otherwise. - */ - [[nodiscard]] bool getDoesAttach() const - { - return attachType != CardRelationType::DoesNotAttach; - } - - /** - * @brief Returns true if this card transforms into another card. - * - * @return True if it transforms, false otherwise. - */ - [[nodiscard]] bool getDoesTransform() const - { - return attachType == CardRelationType::TransformInto; - } - - /** - * @brief Returns a string description of the attachment type. - * - * @return "attach" for AttachTo, "transform" for TransformInto, empty string otherwise. - */ - [[nodiscard]] QString getAttachTypeAsString() const - { - return cardAttachTypeToString(attachType); - } - - /** - * @brief Determines whether another instance can be created. - * - * @return True if creation is allowed, false if constrained by attachment. - */ - [[nodiscard]] bool getCanCreateAnother() const - { - return !getDoesAttach(); - } - - /** - * @brief Returns whether this relation is excluded from "create all" operations. - * - * @return True if excluded, false otherwise. - */ - [[nodiscard]] bool getIsCreateAllExclusion() const - { - return isCreateAllExclusion; - } - - /** - * @brief Returns whether the relation count is variable. - * - * @return True if variable, false otherwise. - */ - [[nodiscard]] bool getIsVariable() const - { - return isVariableCount; - } - - /** - * @brief Returns the default count of related cards. - * - * @return Integer representing default number. - */ - [[nodiscard]] int getDefaultCount() const - { - return defaultCount; - } - - /** - * @brief Returns whether the relation is persistent. - * - * Persistent relations are not destroyed on zone changes. - * - * @return True if persistent, false otherwise. - */ - [[nodiscard]] bool getIsPersistent() const - { - return isPersistent; - } -}; - -#endif // COCKATRICE_CARD_RELATION_H diff --git a/libcockatrice_card/libcockatrice/card/relation/card_relation_type.h b/libcockatrice_card/libcockatrice/card/relation/card_relation_type.h deleted file mode 100644 index 9bd85f3ac..000000000 --- a/libcockatrice_card/libcockatrice/card/relation/card_relation_type.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef COCKATRICE_CARD_RELATION_TYPE_H -#define COCKATRICE_CARD_RELATION_TYPE_H - -#include - -/** - * @enum CardRelationType - * @ingroup Cards - * @brief Types of attachments between cards. - * - * DoesNotAttach: No attachment is present. - * AttachTo: This card attaches to another card. - * TransformInto: This card transforms into another card. - */ -enum class CardRelationType -{ - DoesNotAttach = 0, - AttachTo = 1, - TransformInto = 2, -}; - -// Helper function to transform the enum values into human-readable strings -inline QString cardAttachTypeToString(CardRelationType type) -{ - switch (type) { - case CardRelationType::AttachTo: - return "attach"; - case CardRelationType::TransformInto: - return "transform"; - default: - return ""; - } -} - -#endif // COCKATRICE_CARD_RELATION_TYPE_H diff --git a/libcockatrice_card/libcockatrice/card/set/card_set.cpp b/libcockatrice_card/libcockatrice/card/set/card_set.cpp deleted file mode 100644 index 20d0aced8..000000000 --- a/libcockatrice_card/libcockatrice/card/set/card_set.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "card_set.h" - -#include -#include - -const char *CardSet::TOKENS_SETNAME = "TK"; - -CardSet::CardSet(ICardSetPriorityController *_priorityController, - const QString &_shortName, - const QString &_longName, - const QString &_setType, - const QDate &_releaseDate, - const CardSet::Priority _priority) - : priorityController(std::move(_priorityController)), shortName(_shortName), longName(_longName), - releaseDate(_releaseDate), setType(_setType), priority(_priority) -{ - loadSetOptions(); -} - -CardSetPtr CardSet::newInstance(ICardSetPriorityController *_priorityController, - const QString &_shortName, - const QString &_longName, - const QString &_setType, - const QDate &_releaseDate, - const Priority _priority) -{ - CardSetPtr ptr(new CardSet(_priorityController, _shortName, _longName, _setType, _releaseDate, _priority)); - // ptr->setSmartPointer(ptr); - return ptr; -} - -QString CardSet::getCorrectedShortName() const -{ - // For Windows machines. - QSet invalidFileNames; - invalidFileNames << "CON" - << "PRN" - << "AUX" - << "NUL" - << "COM1" - << "COM2" - << "COM3" - << "COM4" - << "COM5" - << "COM6" - << "COM7" - << "COM8" - << "COM9" - << "LPT1" - << "LPT2" - << "LPT3" - << "LPT4" - << "LPT5" - << "LPT6" - << "LPT7" - << "LPT8" - << "LPT9"; - - return invalidFileNames.contains(shortName) ? shortName + "_" : shortName; -} - -void CardSet::loadSetOptions() -{ - sortKey = priorityController->getSortKey(shortName); - enabled = priorityController->isEnabled(shortName); - isknown = priorityController->isKnown(shortName); -} - -void CardSet::setSortKey(unsigned int _sortKey) -{ - sortKey = _sortKey; - priorityController->setSortKey(shortName, _sortKey); -} - -void CardSet::setEnabled(bool _enabled) -{ - enabled = _enabled; - priorityController->setEnabled(shortName, _enabled); -} - -void CardSet::setIsKnown(bool _isknown) -{ - isknown = _isknown; - priorityController->setIsKnown(shortName, _isknown); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/set/card_set.h b/libcockatrice_card/libcockatrice/card/set/card_set.h deleted file mode 100644 index fe4b66522..000000000 --- a/libcockatrice_card/libcockatrice/card/set/card_set.h +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef COCKATRICE_CARD_SET_H -#define COCKATRICE_CARD_SET_H - -#include -#include -#include -#include -#include - -class CardInfo; -using CardInfoPtr = QSharedPointer; - -class CardSet; -using CardSetPtr = QSharedPointer; - -/** - * @class CardSet - * @ingroup CardSets - * - * @brief A collection of cards grouped under a common identifier. - * - * A set serves both as metadata (identifier, title, category, release date, and priority) - * and as a container of all cards that belong to it. Each set can be enabled/disabled - * and marked as known/unknown depending on context. - * - * The class inherits from `QList`, so it can be iterated over directly - * to access its contents. - * - * Typical usage: - * - Query metadata such as identifier, category, or release date. - * - Enable or disable sets according to user preference. - * - Store and retrieve CardInfo objects associated with the set. - */ -class CardSet : public QList -{ -public: - /** - * @enum Priority - * @brief Defines relative ordering and importance of sets. - */ - enum Priority - { - PriorityFallback = 0, ///< Used when no other priority is defined. - PriorityPrimary = 10, ///< Primary, canonical set. - PrioritySecondary = 20, ///< Secondary but relevant. - PriorityReprint = 30, ///< Duplicate or reprint category. - PriorityOther = 40, ///< Miscellaneous grouping. - PriorityLowest = 100, ///< Lowest sorting priority. - }; - - static const char *TOKENS_SETNAME; ///< Reserved identifier for token-like sets. - -private: - ICardSetPriorityController *priorityController; ///< Interface to the card set priority controller. - QString shortName; ///< Short identifier for the set. - QString longName; ///< Full name for the set. - unsigned int sortKey; ///< Custom numeric sort key. - QDate releaseDate; ///< Release date, may be empty if unknown. - QString setType; ///< Type/category label for the set. - Priority priority; ///< Priority level for sorting and relevance. - bool enabled; ///< Whether the set is active/enabled. - bool isknown; ///< Whether the set is considered known. - -public: - /** - * @brief Constructs a CardSet. - * - * @param priorityController Interface to a card set priority controller. - * @param _shortName Identifier string. - * @param _longName Full descriptive name. - * @param _setType Type/category string. - * @param _releaseDate Release date (optional). - * @param _priority Sorting/priority level. - */ - explicit CardSet(ICardSetPriorityController *priorityController, - const QString &_shortName = QString(), - const QString &_longName = QString(), - const QString &_setType = QString(), - const QDate &_releaseDate = QDate(), - const Priority _priority = PriorityFallback); - - /** - * @brief Creates and returns a new shared CardSet instance. - * - * @param priorityController Interface to a card set priority controller. - * @param _shortName Identifier string. - * @param _longName Full descriptive name. - * @param _setType Type/category string. - * @param _releaseDate Release date (optional). - * @param _priority Sorting/priority level. - * @return A shared pointer to the new CardSet. - */ - static CardSetPtr newInstance(ICardSetPriorityController *priorityController, - const QString &_shortName = QString(), - const QString &_longName = QString(), - const QString &_setType = QString(), - const QDate &_releaseDate = QDate(), - const Priority _priority = PriorityFallback); - - /** - * @brief Returns a safe, sanitized version of the short name. - * - * Intended for file paths or identifiers where only certain - * characters are allowed. - * - * @return Sanitized short name. - */ - [[nodiscard]] QString getCorrectedShortName() const; - - /// @return Short identifier of the set. - [[nodiscard]] QString getShortName() const - { - return shortName; - } - - /// @return Descriptive name of the set. - [[nodiscard]] QString getLongName() const - { - return longName; - } - - /// @return Type/category string of the set. - [[nodiscard]] QString getSetType() const - { - return setType; - } - - /// @return Release date of the set. - [[nodiscard]] QDate getReleaseDate() const - { - return releaseDate; - } - - /// @return Priority level of the set. - [[nodiscard]] Priority getPriority() const - { - return priority; - } - - /** - * @brief Sets the full name of the set. - * @param _longName New full name. - */ - void setLongName(const QString &_longName) - { - longName = _longName; - } - - /** - * @brief Sets the category/type of the set. - * @param _setType New category string. - */ - void setSetType(const QString &_setType) - { - setType = _setType; - } - - /** - * @brief Sets the release date of the set. - * @param _releaseDate New release date. - */ - void setReleaseDate(const QDate &_releaseDate) - { - releaseDate = _releaseDate; - } - - /** - * @brief Updates the priority of the set. - * @param _priority New priority value. - */ - - void setPriority(const Priority _priority) - { - priority = _priority; - } - - /** - * @brief Loads state values (enabled, known, sort key) from configuration. - * - * Reads external configuration and applies it to this set. - */ - void loadSetOptions(); - - /// @return The sort key assigned to this set. - [[nodiscard]] int getSortKey() const - { - return sortKey; - } - - /** - * @brief Assigns a new sort key to this set. - * @param _sortKey The numeric key to use for sorting. - */ - void setSortKey(unsigned int _sortKey); - - /// @return True if the set is enabled. - [[nodiscard]] bool getEnabled() const - { - return enabled; - } - - /** - * @brief Enables or disables the set. - * @param _enabled True to enable, false to disable. - */ - void setEnabled(bool _enabled); - - /// @return True if the set is considered known. - [[nodiscard]] bool getIsKnown() const - { - return isknown; - } - - /** - * @brief Marks the set as known or unknown. - * @param _isknown True if known, false if unknown. - */ - void setIsKnown(bool _isknown); - - /** - * @brief Determines whether the set has incomplete metadata and should be ignored. - * - * @return True if the long name, type, and release date are all empty. - */ - [[nodiscard]] bool getIsKnownIgnored() const - { - return longName.length() + setType.length() + releaseDate.toString().length() == 0; - } -}; - -#endif // COCKATRICE_CARD_SET_H diff --git a/libcockatrice_card/libcockatrice/card/set/card_set_comparator.h b/libcockatrice_card/libcockatrice/card/set/card_set_comparator.h deleted file mode 100644 index 96f1052a9..000000000 --- a/libcockatrice_card/libcockatrice/card/set/card_set_comparator.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file card_set_comparator.h - * @ingroup CardSets - * @brief TODO: Document this. - */ - -#ifndef SET_PRIORITY_COMPARATOR_H -#define SET_PRIORITY_COMPARATOR_H - -#include "../card_info.h" - -class SetPriorityComparator -{ -public: - /* - * Returns true if a has higher download priority than b - * Enabled sets have priority over disabled sets - * Both groups follow the user-defined order - */ - inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const - { - if (a->getEnabled()) { - return !b->getEnabled() || a->getSortKey() < b->getSortKey(); - } else { - return !b->getEnabled() && a->getSortKey() < b->getSortKey(); - } - } -}; - -class SetReleaseDateComparator -{ -public: - /* - * Returns true if a has higher download priority than b - * Enabled sets have priority over disabled sets - * Both groups follow the user-defined order - */ - inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const - { - if (a->getEnabled()) { - return !b->getEnabled() || a->getReleaseDate() < b->getReleaseDate(); - } else { - return !b->getEnabled() && a->getReleaseDate() < b->getReleaseDate(); - } - } -}; - -class CardSetPriorityComparator -{ -public: - /* - * Returns true if a has higher download priority than b - * Enabled sets have priority over disabled sets - * Both groups follow the user-defined order - */ - inline bool operator()(const PrintingInfo &a, const PrintingInfo &b) const - { - if (a.getSet()->getEnabled()) { - return !b.getSet()->getEnabled() || a.getSet()->getSortKey() < b.getSet()->getSortKey(); - } else { - return !b.getSet()->getEnabled() && a.getSet()->getSortKey() < b.getSet()->getSortKey(); - } - } -}; - -#endif // SET_PRIORITY_COMPARATOR_H \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/set/card_set_list.cpp b/libcockatrice_card/libcockatrice/card/set/card_set_list.cpp deleted file mode 100644 index 8d6aa7365..000000000 --- a/libcockatrice_card/libcockatrice/card/set/card_set_list.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "card_set_list.h" - -class CardSetList::KeyCompareFunctor -{ -public: - inline bool operator()(const CardSetPtr &a, const CardSetPtr &b) const - { - if (a.isNull() || b.isNull()) { - // qCWarning(CardInfoLog) << "SetList::KeyCompareFunctor a or b is null"; - return false; - } - - return a->getSortKey() < b->getSortKey(); - } -}; - -void CardSetList::sortByKey() -{ - std::sort(begin(), end(), KeyCompareFunctor()); -} - -int CardSetList::getEnabledSetsNum() -{ - int num = 0; - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && set->getEnabled()) { - ++num; - } - } - return num; -} - -int CardSetList::getUnknownSetsNum() -{ - int num = 0; - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - ++num; - } - } - return num; -} - -QStringList CardSetList::getUnknownSetsNames() -{ - QStringList sets = QStringList(); - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - sets << set->getShortName(); - } - } - return sets; -} - -void CardSetList::enableAllUnknown() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - set->setIsKnown(true); - set->setEnabled(true); - } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { - set->setEnabled(true); - } - } -} - -void CardSetList::enableAll() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - - if (set == nullptr) { - // qCWarning(CardInfoLog) << "enabledAll has null"; - continue; - } - - if (!set->getIsKnownIgnored()) { - set->setIsKnown(true); - } - - set->setEnabled(true); - } -} - -void CardSetList::markAllAsKnown() -{ - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set && !set->getIsKnown() && !set->getIsKnownIgnored()) { - set->setIsKnown(true); - set->setEnabled(false); - } else if (set && set->getIsKnownIgnored() && !set->getEnabled()) { - set->setEnabled(true); - } - } -} - -void CardSetList::guessSortKeys() -{ - defaultSort(); - for (int i = 0; i < size(); ++i) { - CardSetPtr set = at(i); - if (set.isNull()) { - // qCWarning(CardInfoLog) << "guessSortKeys set is null"; - continue; - } - set->setSortKey(i); - } -} - -void CardSetList::defaultSort() -{ - std::sort(begin(), end(), [](const CardSetPtr &a, const CardSetPtr &b) { - // Sort by priority, then by release date, then by short name - if (a->getPriority() != b->getPriority()) { - return a->getPriority() < b->getPriority(); // lowest first - } else if (a->getReleaseDate() != b->getReleaseDate()) { - return a->getReleaseDate() > b->getReleaseDate(); // most recent first - } else { - return a->getShortName() < b->getShortName(); // alphabetically - } - }); -} \ No newline at end of file diff --git a/libcockatrice_card/libcockatrice/card/set/card_set_list.h b/libcockatrice_card/libcockatrice/card/set/card_set_list.h deleted file mode 100644 index 81d605374..000000000 --- a/libcockatrice_card/libcockatrice/card/set/card_set_list.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef COCKATRICE_CARD_SET_LIST_H -#define COCKATRICE_CARD_SET_LIST_H - -#include "card_set.h" - -#include - -/** - * @class CardSetList - * @ingroup CardSets - * - * @brief A list-like container for CardSet objects with extended management methods. - * - * Extends `QList` by adding convenience operations for sorting, - * enabling/disabling sets, and tracking known/unknown status. Unlike a plain - * list, this container provides domain-specific functionality for handling - * groups of sets in bulk. - */ -class CardSetList : public QList -{ -private: - /** - * @class KeyCompareFunctor - * @brief Internal comparison functor for sorting by sort key. - * - * Used internally by `sortByKey()` to order sets consistently - * according to their assigned numeric sort keys. - */ - class KeyCompareFunctor; - -public: - /** - * @brief Sorts the set list by each set’s assigned sort key. - * - * Uses KeyCompareFunctor internally. If two sets share the - * same sort key, their relative order is unspecified. - */ - void sortByKey(); - - /** - * @brief Reassigns sort keys based on the current order. - * - * Calls defaultSort() and then assigns sequential sort keys - * to all sets according to their resulting positions, replacing - * any existing sort keys to ensure consistent ordering. - */ - void guessSortKeys(); - - /** - * @brief Enables all sets that are unknown or ignored. - * - * Sets that are not marked as known and not ignored are marked as known - * and enabled. Ignored-known sets are also enabled, but remain ignored. - */ - void enableAllUnknown(); - - /** - * @brief Enables all sets in the list. - * - * Equivalent to calling `setEnabled(true)` on each entry. - */ - void enableAll(); - - /** - * @brief Marks all sets as known and adjusts their enabled state. - * - * Unknown, non-ignored sets become known and disabled. - * Ignored-known sets are enabled if they were previously disabled. - */ - void markAllAsKnown(); - - /** - * @brief Counts the number of sets that are currently enabled. - * - * @return Integer count of enabled sets. - */ - int getEnabledSetsNum(); - - /** - * @brief Counts the number of sets that are currently unknown. - * - * @return Integer count of unknown sets. - */ - int getUnknownSetsNum(); - - /** - * @brief Collects the short names of all sets marked as unknown. - * - * @return A list of unknown set names. - */ - QStringList getUnknownSetsNames(); - - /** - * @brief Sorts the list by default rules. - * - * Orders sets first by priority (ascending), then by release date - * (most recent first), and finally alphabetically by short name. - */ - void defaultSort(); -}; - -#endif // COCKATRICE_CARD_SET_LIST_H diff --git a/libcockatrice_deck_list/CMakeLists.txt b/libcockatrice_deck_list/CMakeLists.txt deleted file mode 100644 index 5ccdb5f66..000000000 --- a/libcockatrice_deck_list/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - libcockatrice/deck_list/tree/abstract_deck_list_card_node.h - libcockatrice/deck_list/tree/abstract_deck_list_node.h - libcockatrice/deck_list/tree/deck_list_card_node.h - libcockatrice/deck_list/tree/inner_deck_list_node.h - libcockatrice/deck_list/deck_list.h - libcockatrice/deck_list/deck_list_history_manager.h - libcockatrice/deck_list/deck_list_node_tree.h - libcockatrice/deck_list/deck_list_memento.h - libcockatrice/deck_list/sideboard_plan.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_deck_list STATIC - ${MOC_SOURCES} - libcockatrice/deck_list/tree/abstract_deck_list_card_node.cpp - libcockatrice/deck_list/tree/abstract_deck_list_node.cpp - libcockatrice/deck_list/tree/deck_list_card_node.cpp - libcockatrice/deck_list/tree/inner_deck_list_node.cpp - libcockatrice/deck_list/deck_list.cpp - libcockatrice/deck_list/deck_list_history_manager.cpp - libcockatrice/deck_list/deck_list_node_tree.cpp - libcockatrice/deck_list/sideboard_plan.cpp -) - -add_dependencies(libcockatrice_deck_list libcockatrice_protocol) - -target_include_directories(libcockatrice_deck_list PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_deck_list PUBLIC libcockatrice_protocol libcockatrice_utility ${QT_CORE_MODULE}) diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp deleted file mode 100644 index e3e7b41c0..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.cpp +++ /dev/null @@ -1,512 +0,0 @@ -#include "deck_list.h" - -#include "deck_list_memento.h" -#include "tree/abstract_deck_list_node.h" -#include "tree/deck_list_card_node.h" -#include "tree/inner_deck_list_node.h" - -#include -#include -#include -#include -#include -#include -#include - -#if QT_VERSION < 0x050600 -// qHash on QRegularExpression was added in 5.6, FIX IT -uint qHash(const QRegularExpression &key, uint seed) noexcept -{ - return qHash(key.pattern(), seed); // call qHash on pattern QString instead -} -#endif - -static const QString CURRENT_SIDEBOARD_PLAN_KEY = ""; - -bool DeckList::Metadata::isEmpty() const -{ - return name.isEmpty() && comments.isEmpty() && bannerCard.isEmpty() && tags.isEmpty(); -} - -DeckList::DeckList() -{ -} - -DeckList::DeckList(const QString &nativeString) -{ - loadFromString_Native(nativeString); -} - -DeckList::DeckList(const Metadata &metadata, - const DecklistNodeTree &tree, - const QMap &sideboardPlans) - : metadata(metadata), sideboardPlans(sideboardPlans), tree(tree) -{ -} - -QList DeckList::getCurrentSideboardPlan() const -{ - if (!sideboardPlans.contains(CURRENT_SIDEBOARD_PLAN_KEY)) { - return {}; - } - - return sideboardPlans.value(CURRENT_SIDEBOARD_PLAN_KEY).getMoveList(); -} - -void DeckList::setCurrentSideboardPlan(const QList &plan) -{ - sideboardPlans[CURRENT_SIDEBOARD_PLAN_KEY].setMoveList(plan); -} - -bool DeckList::readElement(QXmlStreamReader *xml) -{ - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "lastLoadedTimestamp") { - metadata.lastLoadedTimestamp = xml->readElementText(); - } else if (childName == "deckname") { - metadata.name = xml->readElementText(); - } else if (childName == "format") { - metadata.gameFormat = xml->readElementText(); - } else if (childName == "comments") { - metadata.comments = xml->readElementText(); - } else if (childName == "bannerCard") { - QString providerId = xml->attributes().value("providerId").toString(); - QString cardName = xml->readElementText(); - metadata.bannerCard = {cardName, providerId}; - } else if (childName == "tags") { - metadata.tags.clear(); // Clear existing tags - while (xml->readNextStartElement()) { - if (xml->name().toString() == "tag") { - metadata.tags.append(xml->readElementText()); - } - } - } else if (childName == "zone") { - tree.readZoneElement(xml); - } else if (childName == "sideboard_plan") { - SideboardPlan newSideboardPlan; - if (newSideboardPlan.readElement(xml)) { - sideboardPlans.insert(newSideboardPlan.getName(), newSideboardPlan); - } - } - } else if (xml->isEndElement() && (childName == "cockatrice_deck")) { - return false; - } - return true; -} - -static void writeMetadata(QXmlStreamWriter *xml, const DeckList::Metadata &metadata) -{ - xml->writeTextElement("lastLoadedTimestamp", metadata.lastLoadedTimestamp); - xml->writeTextElement("deckname", metadata.name); - xml->writeTextElement("format", metadata.gameFormat); - xml->writeStartElement("bannerCard"); - xml->writeAttribute("providerId", metadata.bannerCard.providerId); - xml->writeCharacters(metadata.bannerCard.name); - xml->writeEndElement(); - xml->writeTextElement("comments", metadata.comments); - - // Write tags - xml->writeStartElement("tags"); - for (const QString &tag : metadata.tags) { - xml->writeTextElement("tag", tag); - } - xml->writeEndElement(); -} - -void DeckList::write(QXmlStreamWriter *xml) const -{ - xml->writeStartElement("cockatrice_deck"); - xml->writeAttribute("version", "1"); - - writeMetadata(xml, metadata); - - // Write zones - tree.write(xml); - - // Write sideboard plans - for (auto &sideboardPlan : sideboardPlans.values()) { - sideboardPlan.write(xml); - } - - xml->writeEndElement(); // Close "cockatrice_deck" -} - -bool DeckList::loadFromXml(QXmlStreamReader *xml) -{ - if (xml->error()) { - qDebug() << "Error loading deck from xml: " << xml->errorString(); - return false; - } - - cleanList(); - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isStartElement()) { - if (xml->name().toString() != "cockatrice_deck") - return false; - while (!xml->atEnd()) { - xml->readNext(); - if (!readElement(xml)) - break; - } - } - } - refreshDeckHash(); - if (xml->error()) { - qDebug() << "Error loading deck from xml: " << xml->errorString(); - return false; - } - return true; -} - -bool DeckList::loadFromString_Native(const QString &nativeString) -{ - QXmlStreamReader xml(nativeString); - return loadFromXml(&xml); -} - -QString DeckList::writeToString_Native() const -{ - QString result; - QXmlStreamWriter xml(&result); - xml.writeStartDocument(); - write(&xml); - xml.writeEndDocument(); - return result; -} - -bool DeckList::loadFromFile_Native(QIODevice *device) -{ - QXmlStreamReader xml(device); - return loadFromXml(&xml); -} - -bool DeckList::saveToFile_Native(QIODevice *device) const -{ - QXmlStreamWriter xml(device); - xml.setAutoFormatting(true); - xml.writeStartDocument(); - - write(&xml); - - xml.writeEndDocument(); - return true; -} - -/** - * Clears the decklist and loads in a new deck from text - * - * @param in The text to load - * @param preserveMetadata If true, don't clear the existing metadata - * @param cardNameNormalizer Function that takes the parsed card name string in the text and - * @return False if the input was empty, true otherwise. - */ -bool DeckList::loadFromStream_Plain(QTextStream &in, - bool preserveMetadata, - const std::function &cardNameNormalizer) -{ - const QRegularExpression reCardLine(R"(^\s*[\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); - const QRegularExpression reEmpty("^\\s*$"); - const QRegularExpression reComment(R"([\w\[\(\{].*$)", QRegularExpression::UseUnicodePropertiesOption); - const QRegularExpression reSBMark("^\\s*sb:\\s*(.+)", QRegularExpression::CaseInsensitiveOption); - const QRegularExpression reSBComment("^sideboard\\b.*$", QRegularExpression::CaseInsensitiveOption); - const QRegularExpression reDeckComment("^((main)?deck(list)?|mainboard)\\b", - QRegularExpression::CaseInsensitiveOption); - - // Regex for advanced card parsing - const QRegularExpression reMultiplier(R"(^[xX\(\[]*(\d+)[xX\*\)\]]* ?(.+))"); - - // Regex for extracting set code and collector number with attached symbols - const QRegularExpression reHyphenFormat(R"(\((\w{3,})\)\s+(\w{3,})-(\d+[^\w\s]*))"); - const QRegularExpression reRegularFormat(R"(\((\w{3,})\)\s+(\d+[^\w\s]*))"); - - cleanList(preserveMetadata); - - auto inputs = in.readAll().trimmed().split('\n'); - auto max_line = inputs.size(); - - // Start at the first empty line before the first card line - auto deckStart = inputs.indexOf(reCardLine); - if (deckStart == -1) { - if (inputs.indexOf(reComment) == -1) { - return false; // Input is empty - } - deckStart = max_line; - } else { - deckStart = inputs.lastIndexOf(reEmpty, deckStart); - if (deckStart == -1) { - deckStart = 0; - } - } - - // find sideboard position, if marks are used this won't be needed - int sBStart = -1; - if (inputs.indexOf(reSBMark, deckStart) == -1) { - sBStart = inputs.indexOf(reSBComment, deckStart); - if (sBStart == -1) { - sBStart = inputs.indexOf(reEmpty, deckStart + 1); - if (sBStart == -1) { - sBStart = max_line; - } - auto nextCard = inputs.indexOf(reCardLine, sBStart + 1); - if (inputs.indexOf(reEmpty, nextCard + 1) != -1) { - sBStart = max_line; - } - } - } - - int index = 0; - QRegularExpressionMatch match; - - // Parse name and comments - while (index < deckStart) { - const auto ¤t = inputs.at(index++); - if (!current.contains(reEmpty)) { - match = reComment.match(current); - metadata.name = match.captured(); - break; - } - } - while (index < deckStart) { - const auto ¤t = inputs.at(index++); - if (!current.contains(reEmpty)) { - match = reComment.match(current); - metadata.comments += match.captured() + '\n'; - } - } - metadata.comments.chop(1); - - // Discard empty lines - while (index < max_line && inputs.at(index).contains(reEmpty)) { - ++index; - } - - // Discard line if it starts with deck or mainboard, all cards until the sideboard starts are in the mainboard - if (inputs.at(index).contains(reDeckComment)) { - ++index; - } - - // Parse decklist - for (; index < max_line; ++index) { - // check if line is a card - match = reCardLine.match(inputs.at(index)); - if (!match.hasMatch()) - continue; - - QString cardName = match.captured().simplified(); - bool sideboard = false; - - // Sideboard detection - if (sBStart < 0) { - match = reSBMark.match(cardName); - if (match.hasMatch()) { - sideboard = true; - cardName = match.captured(1); - } - } else { - if (index == sBStart) - continue; - sideboard = index > sBStart; - } - - // Extract set code, collector number, and foil - QString setCode; - QString collectorNumber; - bool isFoil = false; - - // Check for foil status at the end of the card name - if (cardName.endsWith("*F*", Qt::CaseInsensitive)) { - isFoil = true; - cardName.chop(3); // Remove the "*F*" from the card name - } - Q_UNUSED(isFoil); - - // Attempt to match the hyphen-separated format (PLST-2094) - match = reHyphenFormat.match(cardName); - if (match.hasMatch()) { - setCode = match.captured(2).toUpper(); - collectorNumber = match.captured(3); - cardName = cardName.left(match.capturedStart()).trimmed(); - } else { - // Attempt to match the regular format (PLST) 2094 - match = reRegularFormat.match(cardName); - if (match.hasMatch()) { - setCode = match.captured(1).toUpper(); - collectorNumber = match.captured(2); - cardName = cardName.left(match.capturedStart()).trimmed(); - } - } - - // check if a specific amount is mentioned - int amount = 1; - match = reMultiplier.match(cardName); - if (match.hasMatch()) { - amount = match.captured(1).toInt(); - cardName = match.captured(2); - } - - // Normalize the card name - cardName = cardNameNormalizer(cardName); - - // Determine the zone (mainboard/sideboard) - QString zoneName = sideboard ? DECK_ZONE_SIDE : DECK_ZONE_MAIN; - - // make new entry in decklist - tree.addCard(cardName, amount, zoneName, -1, setCode, collectorNumber); - } - - refreshDeckHash(); - return true; -} - -bool DeckList::loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer) -{ - QTextStream in(device); - return loadFromStream_Plain(in, false, cardNameNormalizer); -} - -bool DeckList::saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const -{ - auto writeToStream = [&stream, prefixSideboardCards, slashTappedOutSplitCards](const auto node, const auto card) { - if (prefixSideboardCards && node->getName() == DECK_ZONE_SIDE) { - stream << "SB: "; - } - if (!slashTappedOutSplitCards) { - stream << QString("%1 %2\n").arg(card->getNumber()).arg(card->getName()); - } else { - stream << QString("%1 %2\n").arg(card->getNumber()).arg(card->getName().replace("//", "/")); - } - }; - - forEachCard(writeToStream); - return true; -} - -bool DeckList::saveToFile_Plain(QIODevice *device, bool prefixSideboardCards, bool slashTappedOutSplitCards) const -{ - QTextStream out(device); - return saveToStream_Plain(out, prefixSideboardCards, slashTappedOutSplitCards); -} - -QString DeckList::writeToString_Plain(bool prefixSideboardCards, bool slashTappedOutSplitCards) const -{ - QString result; - QTextStream out(&result); - saveToStream_Plain(out, prefixSideboardCards, slashTappedOutSplitCards); - return result; -} - -/** - * Clears all cards and other data from the decklist - * - * @param preserveMetadata If true, only clear the cards - */ -void DeckList::cleanList(bool preserveMetadata) -{ - tree.clear(); - if (!preserveMetadata) { - metadata = {}; - } - refreshDeckHash(); -} - -QStringList DeckList::getCardList(const QSet &restrictToZones) const -{ - auto nodes = tree.getCardNodes(restrictToZones); - - QStringList result; - std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result), [](auto node) { return node->getName(); }); - - return result; -} - -QList DeckList::getCardRefList(const QSet &restrictToZones) const -{ - auto nodes = tree.getCardNodes(restrictToZones); - - QList result; - std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(result), - [](auto node) { return node->toCardRef(); }); - - return result; -} - -QList DeckList::getCardNodes(const QSet &restrictToZones) const -{ - return tree.getCardNodes(restrictToZones); -} - -QList DeckList::getZoneNodes(const QSet &restrictToZones) const -{ - return tree.getZoneNodes(restrictToZones); -} - -int DeckList::getSideboardSize() const -{ - auto cards = tree.getCardNodes({DECK_ZONE_SIDE}); - - int size = 0; - for (auto card : cards) { - size += card->getNumber(); - } - - return size; -} - -DecklistCardNode *DeckList::addCard(const QString &cardName, - const QString &zoneName, - int position, - const QString &cardSetName, - const QString &cardSetCollectorNumber, - const QString &cardProviderId, - bool formatLegal) -{ - auto node = - tree.addCard(cardName, 1, zoneName, position, cardSetName, cardSetCollectorNumber, cardProviderId, formatLegal); - refreshDeckHash(); - return node; -} - -/** - * Gets the deck hash. - * The hash is computed on the first call to this method, and is cached until the decklist is modified. - * - * @return The deck hash - */ -QString DeckList::getDeckHash() const -{ - if (!cachedDeckHash.isEmpty()) { - return cachedDeckHash; - } - - cachedDeckHash = tree.computeDeckHash(); - return cachedDeckHash; -} - -/** - * Invalidates the cached deckHash. - */ -void DeckList::refreshDeckHash() -{ - cachedDeckHash = QString(); -} - -/** - * Calls a given function on each card in the deck. - */ -void DeckList::forEachCard(const std::function &func) const -{ - tree.forEachCard(func); -} - -DeckListMemento DeckList::createMemento(const QString &reason) const -{ - return DeckListMemento(writeToString_Native(), reason); -} - -void DeckList::restoreMemento(const DeckListMemento &m) -{ - cleanList(); - loadFromString_Native(m.getMemento()); -} diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h deleted file mode 100644 index a96adeb38..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list.h +++ /dev/null @@ -1,257 +0,0 @@ -/** - * @file deck_list.h - * @brief Defines the DeckList class, which manages a full - * deck structure including cards, zones, sideboard plans, and - * serialization to/from multiple formats. This is a logic class which - * does not care about Qt or user facing views. - * See @c DeckListModel for the actual Qt Model to be used for views - */ - -#ifndef DECKLIST_H -#define DECKLIST_H - -#include "deck_list_memento.h" -#include "deck_list_node_tree.h" -#include "sideboard_plan.h" -#include "tree/inner_deck_list_node.h" - -#include -#include -#include -#include - -class AbstractDecklistNode; -class DecklistCardNode; -class CardDatabase; -class QIODevice; -class QTextStream; -class InnerDecklistNode; - -/** - * @class DeckList - * @ingroup Decks - * @brief Represents a complete deck, including metadata, zones, cards, - * and sideboard plans. - * - * A DeckList is a wrapper around an `InnerDecklistNode` tree, - * enriched with metadata like deck name, comments, tags, banner card, - * and multiple sideboard plans. - * - * ### Core responsibilities: - * - Store and manage the root node tree (zones → groups → cards). - * - Provide deck-level metadata (name, comments, tags, banner). - * - Support multiple sideboard plans (meta-game strategies). - * - Provide import/export in multiple formats: - * - Cockatrice native XML format. - * - Plain-text list format. - * - Provide hashing for deck identity (deck hash). - * - * ### Ownership: - * - Owns the `DecklistNodeTree`. - * - Owns `SideboardPlan` instances stored in `sideboardPlans`. - * - * ### Example workflow: - * ``` - * DeckList deck; - * deck.setName("Mono Red Aggro"); - * deck.addCard("Lightning Bolt", "main"); - * deck.addTag("Aggro"); - * deck.saveToFile_Native(device); - * ``` - */ -class DeckList -{ -public: - struct Metadata - { - QString name; ///< User-defined deck name. - QString comments; ///< Free-form comments or notes. - QString gameFormat; ///< The name of the game format this deck contains legal cards for - CardRef bannerCard; ///< Optional representative card for the deck. - QStringList tags; ///< User-defined tags for deck classification. - QString lastLoadedTimestamp; ///< Timestamp string of last load. - - /** - * @brief Checks if all values (except for lastLoadedTimestamp) in the metadata is empty. - */ - bool isEmpty() const; - }; - -private: - Metadata metadata; ///< Deck metadata that is stored in the deck file - QMap sideboardPlans; ///< Named sideboard plans. - DecklistNodeTree tree; ///< The deck tree (zones + cards). - - /** - * @brief Cached deck hash, recalculated lazily. - * An empty string indicates the cache is invalid. - */ - mutable QString cachedDeckHash; - -public: - /// @name Metadata setters - ///@{ - void setName(const QString &_name = QString()) - { - metadata.name = _name; - } - void setComments(const QString &_comments = QString()) - { - metadata.comments = _comments; - } - void setTags(const QStringList &_tags = QStringList()) - { - metadata.tags = _tags; - } - void addTag(const QString &_tag) - { - metadata.tags.append(_tag); - } - void clearTags() - { - metadata.tags.clear(); - } - void setBannerCard(const CardRef &_bannerCard = {}) - { - metadata.bannerCard = _bannerCard; - } - void setLastLoadedTimestamp(const QString &_lastLoadedTimestamp = QString()) - { - metadata.lastLoadedTimestamp = _lastLoadedTimestamp; - } - void setGameFormat(const QString &_gameFormat = QString()) - { - metadata.gameFormat = _gameFormat; - } - ///@} - - /// @brief Construct an empty deck. - explicit DeckList(); - /// @brief Construct from a serialized native-format string. - explicit DeckList(const QString &nativeString); - /// @brief Construct from components - DeckList(const Metadata &metadata, - const DecklistNodeTree &tree, - const QMap &sideboardPlans = {}); - - /** - * @brief Gets a pointer to the underlying node tree. - * Note: DO NOT call this method unless the object needs to have access to the underlying model. - * For now, only the DeckListModel should be calling this. - */ - DecklistNodeTree *getTree() - { - return &tree; - } - - /// @name Metadata getters - /// The individual metadata getters still exist for backwards compatibility. - ///@{ - //! \todo Figure out when we can remove them. - const Metadata &getMetadata() const - { - return metadata; - } - QString getName() const - { - return metadata.name; - } - QString getComments() const - { - return metadata.comments; - } - QStringList getTags() const - { - return metadata.tags; - } - CardRef getBannerCard() const - { - return metadata.bannerCard; - } - QString getLastLoadedTimestamp() const - { - return metadata.lastLoadedTimestamp; - } - QString getGameFormat() const - { - return metadata.gameFormat; - } - ///@} - - bool isBlankDeck() const - { - return metadata.isEmpty() && getCardList().isEmpty(); - } - - /// @name Sideboard plans - ///@{ - QList getCurrentSideboardPlan() const; - void setCurrentSideboardPlan(const QList &plan); - const QMap &getSideboardPlans() const - { - return sideboardPlans; - } - ///@} - - /// @name Serialization (XML) - ///@{ - bool readElement(QXmlStreamReader *xml); - void write(QXmlStreamWriter *xml) const; - bool loadFromXml(QXmlStreamReader *xml); - bool loadFromString_Native(const QString &nativeString); - QString writeToString_Native() const; - bool loadFromFile_Native(QIODevice *device); - bool saveToFile_Native(QIODevice *device) const; - ///@} - - /// @name Serialization (Plain text) - ///@{ - bool loadFromStream_Plain(QTextStream &stream, - bool preserveMetadata, - const std::function &cardNameNormalizer); - bool loadFromFile_Plain(QIODevice *device, const std::function &cardNameNormalizer); - bool saveToStream_Plain(QTextStream &stream, bool prefixSideboardCards, bool slashTappedOutSplitCards) const; - bool - saveToFile_Plain(QIODevice *device, bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const; - QString writeToString_Plain(bool prefixSideboardCards = true, bool slashTappedOutSplitCards = false) const; - ///@} - - /// @name Deck manipulation - ///@{ - void cleanList(bool preserveMetadata = false); - bool isEmpty() const - { - return tree.isEmpty() && metadata.isEmpty() && sideboardPlans.isEmpty(); - } - QStringList getCardList(const QSet &restrictToZones = {}) const; - QList getCardRefList(const QSet &restrictToZones = {}) const; - QList getCardNodes(const QSet &restrictToZones = {}) const; - QList getZoneNodes(const QSet &restrictToZones = {}) const; - int getSideboardSize() const; - - DecklistCardNode *addCard(const QString &cardName, - const QString &zoneName, - int position = -1, - const QString &cardSetName = QString(), - const QString &cardSetCollectorNumber = QString(), - const QString &cardProviderId = QString(), - const bool formatLegal = true); - ///@} - - /// @name Deck identity - ///@{ - QString getDeckHash() const; - void refreshDeckHash(); - ///@} - - /** - * @brief Apply a function to every card in the deck tree. - * - * @param func Function taking (zone node, card node). - */ - void forEachCard(const std::function &func) const; - DeckListMemento createMemento(const QString &reason) const; - void restoreMemento(const DeckListMemento &m); -}; - -#endif diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.cpp deleted file mode 100644 index 83c9cc0bb..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "deck_list_history_manager.h" - -void DeckListHistoryManager::save(const DeckListMemento &memento) -{ - undoStack.push(memento); - redoStack.clear(); - emit undoRedoStateChanged(); -} - -void DeckListHistoryManager::clear() -{ - undoStack.clear(); - redoStack.clear(); - emit undoRedoStateChanged(); -} - -void DeckListHistoryManager::undo(DeckList *deck) -{ - if (undoStack.isEmpty()) - return; - - // Peek at the memento we are going to restore - const DeckListMemento &mementoToRestore = undoStack.top(); - - // Save current state for redo - DeckListMemento currentState = deck->createMemento(mementoToRestore.getReason()); - redoStack.push(currentState); - - // Pop the last state from undo stack and restore it - DeckListMemento memento = undoStack.pop(); - deck->restoreMemento(memento); - - emit undoRedoStateChanged(); -} - -void DeckListHistoryManager::redo(DeckList *deck) -{ - if (redoStack.isEmpty()) - return; - - // Peek at the memento we are going to restore - const DeckListMemento &mementoToRestore = redoStack.top(); - - // Save current state for undo - DeckListMemento currentState = deck->createMemento(mementoToRestore.getReason()); - undoStack.push(currentState); - - // Pop the next state from redo stack and restore it - DeckListMemento memento = redoStack.pop(); - deck->restoreMemento(memento); - - emit undoRedoStateChanged(); -} diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.h deleted file mode 100644 index e6bd27e2d..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_history_manager.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef COCKATRICE_DECK_LIST_HISTORY_MANAGER_H -#define COCKATRICE_DECK_LIST_HISTORY_MANAGER_H - -#include "deck_list.h" -#include "deck_list_memento.h" - -#include -#include - -class DeckListHistoryManager : public QObject -{ - Q_OBJECT - -signals: - void undoRedoStateChanged(); - -public: - explicit DeckListHistoryManager(QObject *parent = nullptr) : QObject(parent) - { - } - - void save(const DeckListMemento &memento); - - void clear(); - - [[nodiscard]] bool canUndo() const - { - return !undoStack.isEmpty(); - } - - [[nodiscard]] bool canRedo() const - { - return !redoStack.isEmpty(); - } - - void undo(DeckList *deck); - - void redo(DeckList *deck); - - [[nodiscard]] QStack getRedoStack() const - { - return redoStack; - } - [[nodiscard]] QStack getUndoStack() const - { - return undoStack; - } - -private: - QStack undoStack; - QStack redoStack; -}; - -#endif // COCKATRICE_DECK_LIST_HISTORY_MANAGER_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_memento.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_memento.h deleted file mode 100644 index 6ddf430c5..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_memento.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COCKATRICE_DECK_LIST_MEMENTO_H -#define COCKATRICE_DECK_LIST_MEMENTO_H -#include - -class DeckListMemento -{ -public: - DeckListMemento() = default; - explicit DeckListMemento(const QString &memento, const QString &reason = QString()) - : memento(memento), reason(reason) - { - } - - [[nodiscard]] QString getMemento() const - { - return memento; - } - [[nodiscard]] QString getReason() const - { - return reason; - } - -private: - QString memento; - QString reason; -}; - -#endif // COCKATRICE_DECK_LIST_MEMENTO_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp deleted file mode 100644 index 644e0851a..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "deck_list_node_tree.h" - -#include "tree/deck_list_card_node.h" - -#include -#include - -DecklistNodeTree::DecklistNodeTree() : root(new InnerDecklistNode()) -{ -} - -DecklistNodeTree::DecklistNodeTree(const DecklistNodeTree &other) : root(new InnerDecklistNode(other.root)) -{ -} - -DecklistNodeTree &DecklistNodeTree::operator=(const DecklistNodeTree &other) -{ - if (this != &other) { - delete root; - root = new InnerDecklistNode(other.root); - } - return *this; -} - -DecklistNodeTree::~DecklistNodeTree() -{ - delete root; -} - -bool DecklistNodeTree::isEmpty() const -{ - return root->isEmpty(); -} - -void DecklistNodeTree::clear() -{ - root->clearTree(); -} - -QList DecklistNodeTree::getCardNodes(const QSet &restrictToZones) const -{ - QList result; - - for (auto *zoneNode : getZoneNodes(restrictToZones)) { - for (auto *cardNode : *zoneNode) { - auto *cardCardNode = dynamic_cast(cardNode); - if (cardCardNode) { - result.append(cardCardNode); - } - } - } - - return result; -} - -QList DecklistNodeTree::getZoneNodes(const QSet &restrictToZones) const -{ - QList zones; - for (auto *node : *root) { - InnerDecklistNode *currentZone = dynamic_cast(node); - if (!currentZone) - continue; - if (!restrictToZones.isEmpty() && !restrictToZones.contains(currentZone->getName())) { - continue; - } - zones.append(currentZone); - } - - return zones; -} - -QString DecklistNodeTree::computeDeckHash() const -{ - auto mainDeckNodes = getCardNodes({DECK_ZONE_MAIN}); - auto sideDeckNodes = getCardNodes({DECK_ZONE_SIDE}); - - static auto nodesToCardList = [](const QList &nodes, const QString &prefix = {}) { - QStringList result; - for (auto node : nodes) { - for (int i = 0; i < node->getNumber(); ++i) { - result.append(prefix + node->getName().toLower()); - } - } - return result; - }; - - QStringList cardList = nodesToCardList(mainDeckNodes) + nodesToCardList(sideDeckNodes, "SB:"); - - cardList.sort(); - QByteArray deckHashArray = QCryptographicHash::hash(cardList.join(";").toUtf8(), QCryptographicHash::Sha1); - quint64 number = (((quint64)(unsigned char)deckHashArray[0]) << 32) + - (((quint64)(unsigned char)deckHashArray[1]) << 24) + - (((quint64)(unsigned char)deckHashArray[2] << 16)) + - (((quint64)(unsigned char)deckHashArray[3]) << 8) + (quint64)(unsigned char)deckHashArray[4]; - return QString::number(number, 32).rightJustified(8, '0'); -} - -void DecklistNodeTree::write(QXmlStreamWriter *xml) const -{ - for (int i = 0; i < root->size(); i++) { - root->at(i)->writeElement(xml); - } -} - -void DecklistNodeTree::readZoneElement(QXmlStreamReader *xml) -{ - QString zoneName = xml->attributes().value("name").toString(); - InnerDecklistNode *newZone = getZoneObjFromName(zoneName); - newZone->readElement(xml); -} - -DecklistCardNode *DecklistNodeTree::addCard(const QString &cardName, - int amount, - const QString &zoneName, - int position, - const QString &cardSetName, - const QString &cardSetCollectorNumber, - const QString &cardProviderId, - const bool formatLegal) -{ - auto *zoneNode = getZoneObjFromName(zoneName); - auto *node = new DecklistCardNode(cardName, amount, zoneNode, position, cardSetName, cardSetCollectorNumber, - cardProviderId, formatLegal); - return node; -} - -bool DecklistNodeTree::deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode) -{ - if (node == root) { - return true; - } - - if (rootNode == nullptr) { - rootNode = root; - } - - int index = rootNode->indexOf(node); - if (index != -1) { - delete rootNode->takeAt(index); - - if (rootNode->empty()) { - deleteNode(rootNode, rootNode->getParent()); - } - - return true; - } - - for (int i = 0; i < rootNode->size(); i++) { - auto *inner = dynamic_cast(rootNode->at(i)); - if (inner) { - if (deleteNode(node, inner)) { - return true; - } - } - } - - return false; -} - -void DecklistNodeTree::forEachCard(const std::function &func) const -{ - // Support for this is only possible if the internal structure - // doesn't get more complicated. - for (int i = 0; i < root->size(); i++) { - InnerDecklistNode *node = dynamic_cast(root->at(i)); - for (int j = 0; j < node->size(); j++) { - DecklistCardNode *card = dynamic_cast(node->at(j)); - func(node, card); - } - } -} - -/** - * Gets the InnerDecklistNode that is the root node for the given zone, creating a new node if it doesn't exist. - */ -InnerDecklistNode *DecklistNodeTree::getZoneObjFromName(const QString &zoneName) const -{ - for (int i = 0; i < root->size(); i++) { - auto *node = dynamic_cast(root->at(i)); - if (node->getName() == zoneName) { - return node; - } - } - - return new InnerDecklistNode(zoneName, root); -} diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h b/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h deleted file mode 100644 index 6de760634..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/deck_list_node_tree.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef COCKATRICE_DECKLIST_NODE_TREE_H -#define COCKATRICE_DECKLIST_NODE_TREE_H - -#include "libcockatrice/utility/card_ref.h" -#include "tree/deck_list_card_node.h" -#include "tree/inner_deck_list_node.h" - -#include - -class DecklistNodeTree -{ - InnerDecklistNode *root; ///< Root of the deck tree (zones + cards). - -public: - /// @brief Constructs an empty DecklistNodeTree - explicit DecklistNodeTree(); - /// @brief Copy constructor. Deep copies the tree - explicit DecklistNodeTree(const DecklistNodeTree &other); - /// @brief Copy-assignment operator. Deep copies the tree - DecklistNodeTree &operator=(const DecklistNodeTree &other); - - virtual ~DecklistNodeTree(); - - /** - * @brief Gets a pointer to the underlying root node. - * Note: DO NOT call this method unless the object needs to have access to the underlying model. - * For now, only the DeckListModel should be calling this. - */ - InnerDecklistNode *getRoot() const - { - return root; - } - - bool isEmpty() const; - - /** - * @brief Deletes all nodes except the root. - */ - void clear(); - - /** - * Gets all card nodes in the tree - * @param restrictToZones Only get the nodes in these zones - * @return A QList containing all the card nodes in the zone. - */ - QList getCardNodes(const QSet &restrictToZones = {}) const; - - /** - * Gets all zone nodes in the tree - * @param restrictToZones If not empty, only get the zone nodes with these names. - * @return A QList containing all the zone nodes in the tree. - */ - QList getZoneNodes(const QSet &restrictToZones = {}) const; - - /** - * @brief Computes the deck hash - */ - QString computeDeckHash() const; - - /** - *@brief Writes the contents of the deck to xml - */ - void write(QXmlStreamWriter *xml) const; - - /** - * @brief Reads a "zone" section of the xml to this tree - */ - void readZoneElement(QXmlStreamReader *xml); - - DecklistCardNode *addCard(const QString &cardName, - int amount, - const QString &zoneName, - int position, - const QString &cardSetName = QString(), - const QString &cardSetCollectorNumber = QString(), - const QString &cardProviderId = QString(), - const bool formatLegal = true); - bool deleteNode(AbstractDecklistNode *node, InnerDecklistNode *rootNode = nullptr); - - /** - * @brief Apply a function to every card in the deck tree. This can modify the cards. - * - * @param func Function taking (zone node, card node). - */ - void forEachCard(const std::function &func) const; - -private: - // Helpers for traversing the tree - InnerDecklistNode *getZoneObjFromName(const QString &zoneName) const; -}; - -#endif // COCKATRICE_DECKLIST_NODE_TREE_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.cpp deleted file mode 100644 index d991ec98e..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "sideboard_plan.h" - -#include - -SideboardPlan::SideboardPlan(const QString &_name, const QList &_moveList) - : name(_name), moveList(_moveList) -{ -} - -void SideboardPlan::setMoveList(const QList &_moveList) -{ - moveList = _moveList; -} - -bool SideboardPlan::readElement(QXmlStreamReader *xml) -{ - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "name") - name = xml->readElementText(); - else if (childName == "move_card_to_zone") { - MoveCard_ToZone m; - while (!xml->atEnd()) { - xml->readNext(); - const QString childName2 = xml->name().toString(); - if (xml->isStartElement()) { - if (childName2 == "card_name") - m.set_card_name(xml->readElementText().toStdString()); - else if (childName2 == "start_zone") - m.set_start_zone(xml->readElementText().toStdString()); - else if (childName2 == "target_zone") - m.set_target_zone(xml->readElementText().toStdString()); - } else if (xml->isEndElement() && (childName2 == "move_card_to_zone")) { - moveList.append(m); - break; - } - } - } - } else if (xml->isEndElement() && (childName == "sideboard_plan")) - return true; - } - return false; -} - -void SideboardPlan::write(QXmlStreamWriter *xml) const -{ - xml->writeStartElement("sideboard_plan"); - xml->writeTextElement("name", name); - for (auto &i : moveList) { - xml->writeStartElement("move_card_to_zone"); - xml->writeTextElement("card_name", QString::fromStdString(i.card_name())); - xml->writeTextElement("start_zone", QString::fromStdString(i.start_zone())); - xml->writeTextElement("target_zone", QString::fromStdString(i.target_zone())); - xml->writeEndElement(); - } - xml->writeEndElement(); -} \ No newline at end of file diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.h b/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.h deleted file mode 100644 index 524217c2d..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/sideboard_plan.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef COCKATRICE_SIDEBOARD_PLAN_H -#define COCKATRICE_SIDEBOARD_PLAN_H - -#include -#include - -class QXmlStreamWriter; -class QXmlStreamReader; - -/** - * @class SideboardPlan - * @ingroup Decks - * @brief Represents a predefined sideboarding strategy for a deck. - * - * Sideboard plans store a named list of card movements that should be applied - * between the mainboard and sideboard for a specific matchup. Each movement - * is expressed using a `MoveCard_ToZone` protobuf message. - * - * ### Responsibilities: - * - Store the plan name and list of moves. - * - Support XML serialization/deserialization. - * - * ### Typical usage: - * A deck can contain multiple sideboard plans (e.g., "vs Aggro", "vs Control"), - * each describing how to transform the main deck into its intended configuration. - */ -class SideboardPlan -{ -private: - QString name; ///< Human-readable name of this plan. - QList moveList; ///< List of move instructions for this plan. - -public: - /** - * @brief Construct a new SideboardPlan. - * @param _name The plan name. - * @param _moveList Initial list of card move instructions. - */ - explicit SideboardPlan(const QString &_name = "", const QList &_moveList = {}); - - /** - * @brief Read a SideboardPlan from an XML stream. - * @param xml XML reader positioned at the plan element. - * @return true if parsing succeeded. - */ - bool readElement(QXmlStreamReader *xml); - - /** - * @brief Write this SideboardPlan to XML. - * @param xml Stream to append the serialized element to. - */ - void write(QXmlStreamWriter *xml) const; - - /// @return The plan name. - [[nodiscard]] QString getName() const - { - return name; - } - - /// @return Const reference to the move list. - [[nodiscard]] const QList &getMoveList() const - { - return moveList; - } - - /// @brief Replace the move list with a new one. - void setMoveList(const QList &_moveList); -}; - -#endif // COCKATRICE_SIDEBOARD_PLAN_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.cpp deleted file mode 100644 index b8a497c20..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "abstract_deck_list_card_node.h" - -bool AbstractDecklistCardNode::compare(AbstractDecklistNode *other) const -{ - switch (sortMethod) { - case ByNumber: - return compareNumber(other); - case ByName: - return compareName(other); - default: - return false; - } -} - -bool AbstractDecklistCardNode::compareNumber(AbstractDecklistNode *other) const -{ - auto *other2 = dynamic_cast(other); - if (other2) { - int n1 = getNumber(); - int n2 = other2->getNumber(); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return true; - } -} - -bool AbstractDecklistCardNode::compareName(AbstractDecklistNode *other) const -{ - auto *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return true; - } -} - -bool AbstractDecklistCardNode::readElement(QXmlStreamReader *xml) -{ - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isEndElement() && xml->name().toString() == "card") - return false; - } - return true; -} - -void AbstractDecklistCardNode::writeElement(QXmlStreamWriter *xml) -{ - xml->writeEmptyElement("card"); - xml->writeAttribute("number", QString::number(getNumber())); - xml->writeAttribute("name", getName()); - - if (!getCardSetShortName().isEmpty()) { - xml->writeAttribute("setShortName", getCardSetShortName()); - } - if (!getCardCollectorNumber().isEmpty()) { - xml->writeAttribute("collectorNumber", getCardCollectorNumber()); - } - if (!getCardProviderId().isEmpty()) { - xml->writeAttribute("uuid", getCardProviderId()); - } -} \ No newline at end of file diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h b/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h deleted file mode 100644 index 88d8b0930..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h +++ /dev/null @@ -1,155 +0,0 @@ -/** - * @file abstract_deck_list_card_node.h - * @brief Defines the AbstractDecklistCardNode base class, which adds - * card-specific behavior on top of AbstractDecklistNode. - * - * This class is the intermediate abstract base between the generic - * AbstractDecklistNode and concrete card entries such as DecklistCardNode - * or DecklistModelCardNode. - */ - -#ifndef COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H -#define COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H - -#include "abstract_deck_list_node.h" - -/** - * @class AbstractDecklistCardNode - * @ingroup DeckModels - * @brief Abstract base class for all deck list nodes that represent - * actual card entries. - * - * While AbstractDecklistNode provides the general interface for all - * nodes in the deck tree (zones, groups, cards), this subclass refines - * the interface to cover properties specific to *cards*: - * - Quantity (number of copies). - * - Name. - * - Set code and collector number. - * - Provider ID. - * - * ### Role in the hierarchy: - * - Leaf-oriented abstract class; no children of its own. - * - Serves as the base for concrete implementations: - * - @c DecklistCardNode: Stores real card data in the deck tree. - * - @c DecklistModelCardNode: Wraps a DecklistCardNode for use - * in the Qt model layer. - * - * ### Responsibilities: - * - Defines getters/setters for all card-identifying attributes. - * - Provides comparison logic for sorting by name or number. - * - Implements XML serialization for saving/loading deck files. - * - * ### Ownership: - * - As with all nodes, owned by its parent InnerDecklistNode. - */ -class AbstractDecklistCardNode : public AbstractDecklistNode -{ -public: - /** - * @brief Construct a new AbstractDecklistCardNode. - * - * @param _parent Optional parent node. If provided, this node - * will be inserted into the parent’s children list. - * @param position Index at which to insert into parent’s children. - * If -1, the node is appended to the end. - */ - explicit AbstractDecklistCardNode(InnerDecklistNode *_parent = nullptr, int position = -1) - : AbstractDecklistNode(_parent, position) - { - } - - /// @return The number of copies of this card in the deck. - [[nodiscard]] virtual int getNumber() const = 0; - - /// @param _number Set the number of copies of this card. - virtual void setNumber(int _number) = 0; - - /// @return The display name of this card. - [[nodiscard]] QString getName() const override = 0; - - /// @param _name Set the display name of this card. - virtual void setName(const QString &_name) = 0; - - /// @return The provider identifier for this card (e.g., UUID). - [[nodiscard]] virtual QString getCardProviderId() const override = 0; - - /// @param _cardProviderId Set the provider identifier for this card. - virtual void setCardProviderId(const QString &_cardProviderId) = 0; - - /// @return The abbreviated set code (e.g., "NEO"). - [[nodiscard]] virtual QString getCardSetShortName() const override = 0; - - /// @param _cardSetShortName Set the abbreviated set code. - virtual void setCardSetShortName(const QString &_cardSetShortName) = 0; - - /// @return The collector number of the card within its set. - [[nodiscard]] virtual QString getCardCollectorNumber() const override = 0; - - /// @param _cardSetNumber Set the collector number. - virtual void setCardCollectorNumber(const QString &_cardSetNumber) = 0; - - /// @return The format legality of the card - virtual bool getFormatLegality() const = 0; - - /// @param _formatLegal If the card is considered legal - virtual void setFormatLegality(const bool _formatLegal) = 0; - - /** - * @brief Get the height of this node in the tree. - * - * For card nodes, height is always 0 because they are leaf nodes - * and do not contain children. - * - * @return 0 - */ - [[nodiscard]] int height() const override - { - return 0; - } - - /** - * @brief Compare this card node against another for sorting. - * - * Uses the node’s current @c sortMethod to determine how to compare: - * - ByName: Alphabetical comparison. - * - ByNumber: Numerical comparison. - * - Default: Falls back to implementation-defined behavior. - * - * @param other Another node to compare against. - * @return true if this node should sort before @p other. - */ - bool compare(AbstractDecklistNode *other) const override; - - /** - * @brief Compare this card node to another by quantity. - * @param other Node to compare against. - * @return true if this node’s number < other’s number. - */ - bool compareNumber(AbstractDecklistNode *other) const; - - /** - * @brief Compare this card node to another by name. - * @param other Node to compare against. - * @return true if this node’s name comes before other’s name. - */ - bool compareName(AbstractDecklistNode *other) const; - - /** - * @brief Deserialize this node’s properties from XML. - * @param xml QXmlStreamReader positioned at the element. - * @return true if parsing succeeded. - * - * This supports loading deck files from Cockatrice’s XML format. - */ - bool readElement(QXmlStreamReader *xml) override; - - /** - * @brief Serialize this node’s properties to XML. - * @param xml Writer to append this node’s XML element. - * - * This supports saving deck files to Cockatrice’s XML format. - */ - void writeElement(QXmlStreamWriter *xml) override; -}; - -#endif // COCKATRICE_ABSTRACT_DECK_LIST_CARD_NODE_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.cpp deleted file mode 100644 index 0f39ce71a..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "abstract_deck_list_node.h" - -#include "inner_deck_list_node.h" - -AbstractDecklistNode::AbstractDecklistNode(InnerDecklistNode *_parent, int position) - : parent(_parent), sortMethod(Default) -{ - if (parent) { - if (position == -1) { - parent->append(this); - } else { - parent->insert(position, this); - } - } -} - -int AbstractDecklistNode::depth() const -{ - if (parent) { - return parent->depth() + 1; - } else { - return 0; - } -} \ No newline at end of file diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.h b/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.h deleted file mode 100644 index 877705705..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_node.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @file abstract_deck_list_node.h - * @brief Defines the AbstractDecklistNode base class used as the foundation - * for all nodes in the deck list tree (zones, groups, and cards). - * - * The deck list is modeled as a tree: - * - The invisible root node is managed by DeckListModel. - * - Top-level children are zones (e.g. Mainboard, Sideboard). - * - Zones contain grouping nodes (e.g. by type, color, or mana cost). - * - Grouping nodes contain card nodes. - * - * This abstract base class provides the interface and shared functionality - * for all node types. Concrete subclasses (InnerDecklistNode, - * DecklistCardNode, DecklistModelCardNode, etc.) implement the specifics. - */ - -#ifndef COCKATRICE_ABSTRACT_DECK_LIST_NODE_H -#define COCKATRICE_ABSTRACT_DECK_LIST_NODE_H - -#include - -/** - * @enum DeckSortMethod - * @ingroup DeckModels - * @brief Defines the different sort strategies a node may use - * to order its children. - * - * Sorting behavior is typically set by the DeckListModel when the user - * requests sorting in the UI. - * - * - ByNumber: Sort numerically (often by collector number). - * - ByName: Sort alphabetically by card name. - * - Default: No explicit sorting; insertion order is preserved. - */ -enum DeckSortMethod -{ - ByNumber, ///< Sort by numeric properties (e.g. collector number). - ByName, ///< Sort by card name (locale-aware comparison). - Default ///< Leave in insertion order. -}; - -class InnerDecklistNode; - -/** - * @class AbstractDecklistNode - * @ingroup DeckModels - * @brief Base class for all nodes in the deck list tree. - * - * This class defines the common interface for every node in the - * deck representation: zones, groupings, and cards. - * - * Responsibilities: - * - Maintain a pointer to its parent (if any). - * - Track the sorting method to be used for child nodes. - * - Provide a consistent interface for retrieving basic identifying - * properties (name, set, collector number, provider ID). - * - Define abstract methods for XML serialization, used when saving - * or loading deck files. - * - * Lifetime / Ownership: - * - Nodes are arranged hierarchically under @c InnerDecklistNode parents. - * - The parent takes ownership of its children; destruction cascades. - * - The DeckListModel holds the invisible root node, which in turn - * owns the entire hierarchy. - * - * Extension: - * - @c InnerDecklistNode is the concrete subclass representing - * "folders" in the tree (zones, groups). - * - @c DecklistCardNode and @c DecklistModelCardNode represent - * actual card entries. - */ -class AbstractDecklistNode -{ -protected: - /** - * @brief Pointer to the parent node, or nullptr if this is the root. - * - * Ownership note: The parent is responsible for destroying this node - * when it is removed from the tree. - */ - InnerDecklistNode *parent; - - /** - * @brief Current sorting strategy for this node's children. - * - * Sorting is applied recursively by the DeckListModel when - * the view requests it. - */ - DeckSortMethod sortMethod; - -public: - /** - * @brief Construct a new AbstractDecklistNode and insert it into its parent. - * - * @param _parent Parent node. May be nullptr if this is the root. - * @param position Optional index at which to insert into the parent's - * children. If -1, the node is appended to the end. - * - * If a parent is provided, the constructor automatically appends - * or inserts this node into the parent’s child list. - */ - explicit AbstractDecklistNode(InnerDecklistNode *_parent = nullptr, int position = -1); - - /// Virtual destructor. Child classes must clean up their resources. - virtual ~AbstractDecklistNode() = default; - - /** - * @brief Set the sort method for this node’s children. - * @param method The sorting strategy to use. - * - * Subclasses may override if they need to apply additional logic. - */ - virtual void setSortMethod(DeckSortMethod method) - { - sortMethod = method; - } - - /** - * @name Core identification properties - * - * These methods provide a standard way for the model to retrieve - * identifying information about a node, regardless of type. - * @{ - */ - [[nodiscard]] virtual QString getName() const = 0; - [[nodiscard]] virtual QString getCardProviderId() const = 0; - [[nodiscard]] virtual QString getCardSetShortName() const = 0; - [[nodiscard]] virtual QString getCardCollectorNumber() const = 0; - /// @} - - /** - * @brief Whether this node is the "deck header" (deck metadata). - * - * This distinguishes special nodes that represent deck-level - * information rather than cards or groupings. - */ - [[nodiscard]] virtual bool isDeckHeader() const = 0; - - /// @return The parent node, or nullptr if this is the root. - [[nodiscard]] InnerDecklistNode *getParent() const - { - return parent; - } - - /** - * @brief Compute the depth of this node in the tree. - * @return Distance from the root (root = 0, children = 1, etc.). - */ - [[nodiscard]] int depth() const; - - /** - * @brief Compute the "height" of this node. - * - * Height is defined by subclasses; it usually represents how - * many levels of descendants this node spans. - * - * For example: - * - A card node has height 1. - * - A group node containing cards has height 2. - */ - [[nodiscard]] virtual int height() const = 0; - - /** - * @brief Compare this node against another for sorting. - * - * The semantics of comparison depend on the node type and the - * current @c sortMethod. - * - * @param other The node to compare against. - * @return true if this node should come before @p other. - */ - virtual bool compare(AbstractDecklistNode *other) const = 0; - - /** - * @name XML serialization - * These methods support reading and writing decks from/to - * Cockatrice deck XML format. - * @{ - */ - virtual bool readElement(QXmlStreamReader *xml) = 0; - virtual void writeElement(QXmlStreamWriter *xml) = 0; - /// @} -}; - -#endif // COCKATRICE_ABSTRACT_DECK_LIST_NODE_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.cpp deleted file mode 100644 index 1791c09a6..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "deck_list_card_node.h" - -DecklistCardNode::DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent) - : AbstractDecklistCardNode(_parent), name(other->getName()), number(other->getNumber()), - cardSetShortName(other->getCardSetShortName()), cardSetNumber(other->getCardCollectorNumber()), - cardProviderId(other->getCardProviderId()) -{ -} \ No newline at end of file diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h b/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h deleted file mode 100644 index b3d42b89a..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h +++ /dev/null @@ -1,186 +0,0 @@ -/** - * @file deck_list_card_node.h - * @brief Defines the DecklistCardNode class, representing a single card entry - * in the deck list tree. - * - * DecklistCardNode is the concrete data-bearing node that corresponds to - * an individual card entry in a deck. It stores the card’s name, quantity, - * set information, and provider ID. These nodes live inside an - * InnerDecklistNode (e.g., under Mainboard → Group → Card). - */ - -#ifndef COCKATRICE_DECK_LIST_CARD_NODE_H -#define COCKATRICE_DECK_LIST_CARD_NODE_H - -#include "abstract_deck_list_card_node.h" - -#include - -/** - * @class DecklistCardNode - * @ingroup DeckModels - * @brief Concrete node type representing an actual card entry in the deck. - * - * This class extends AbstractDecklistCardNode to hold all information - * needed to uniquely identify a card printing within the deck. - * - * ### Role in the hierarchy: - * - Child of an InnerDecklistNode (which groups cards by zone or criteria). - * - Leaf node in the deck tree; it does not contain further children. - * - * ### Data stored: - * - @c name: Card’s display name. - * - @c number: Quantity of this card in the deck. - * - @c cardSetShortName: Abbreviation of the set (e.g., "NEO" for Neon Dynasty). - * - @c cardSetNumber: Collector number within the set. - * - @c cardProviderId: External provider identifier (e.g., UUID or MTGJSON ID). - * - * ### Usage: - * - Constructed directly when building a deck list from user input or file. - * - Used by DeckListModel to present cards in Qt views. - * - Convertible to @c CardRef for database lookups or cross-references. - * - * ### Ownership: - * - Owned by its parent InnerDecklistNode. - * - Destroyed automatically when its parent is destroyed. - */ -class DecklistCardNode : public AbstractDecklistCardNode -{ - QString name; ///< Display name of the card. - int number; ///< Quantity of this card in the deck. - QString cardSetShortName; ///< Short set code (e.g., "NEO"). - QString cardSetNumber; ///< Collector number within the set. - QString cardProviderId; ///< External provider identifier (e.g., UUID). - bool formatLegal; ///< Format legality - -public: - /** - * @brief Construct a new DecklistCardNode. - * - * @param _name Display name of the card. - * @param _number Quantity of this card (default = 1). - * @param _parent Parent node in the tree (zone or group). May be nullptr. - * @param position Index to insert into parent’s children. -1 = append. - * @param _cardSetShortName Short set code (e.g., "NEO"). - * @param _cardSetNumber Collector number within the set. - * @param _cardProviderId External provider ID (e.g., UUID). - * @param _formatLegality If the card is legal in the format - * - * On construction, if a parent is provided, this node is inserted into - * the parent’s children list automatically. - */ - explicit DecklistCardNode(QString _name = QString(), - int _number = 1, - InnerDecklistNode *_parent = nullptr, - int position = -1, - QString _cardSetShortName = QString(), - QString _cardSetNumber = QString(), - QString _cardProviderId = QString(), - bool _formatLegality = true) - : AbstractDecklistCardNode(_parent, position), name(std::move(_name)), number(_number), - cardSetShortName(std::move(_cardSetShortName)), cardSetNumber(std::move(_cardSetNumber)), - cardProviderId(std::move(_cardProviderId)), formatLegal(_formatLegality) - { - } - - /** - * @brief Copy constructor with new parent assignment. - * @param other Existing DecklistCardNode to copy. - * @param _parent Parent node for the copy. - * - * Creates a deep copy of the card node’s properties, but attaches - * the new instance to a different parent in the tree. - */ - explicit DecklistCardNode(DecklistCardNode *other, InnerDecklistNode *_parent); - - /// @return The quantity of this card. - [[nodiscard]] int getNumber() const override - { - return number; - } - - /// @param _number Set the quantity of this card. - void setNumber(int _number) override - { - number = _number; - } - - /// @return The display name of this card. - [[nodiscard]] QString getName() const override - { - return name; - } - - /// @param _name Set the display name of this card. - void setName(const QString &_name) override - { - name = _name; - } - - /// @return The provider identifier for this card. - [[nodiscard]] QString getCardProviderId() const override - { - return cardProviderId; - } - - /// @param _providerId Set the provider identifier for this card. - void setCardProviderId(const QString &_providerId) override - { - cardProviderId = _providerId; - } - - /// @return The short set code (e.g., "NEO"). - [[nodiscard]] QString getCardSetShortName() const override - { - return cardSetShortName; - } - - /// @param _cardSetShortName Set the short set code. - void setCardSetShortName(const QString &_cardSetShortName) override - { - cardSetShortName = _cardSetShortName; - } - - /// @return The collector number of this card within its set. - [[nodiscard]] QString getCardCollectorNumber() const override - { - return cardSetNumber; - } - - /// @param _cardSetNumber Set the collector number. - void setCardCollectorNumber(const QString &_cardSetNumber) override - { - cardSetNumber = _cardSetNumber; - } - - /// @return The format legality of the card - [[nodiscard]] bool getFormatLegality() const override - { - return formatLegal; - } - - /// @param _formatLegal If the card is considered legal - void setFormatLegality(const bool _formatLegal) override - { - formatLegal = _formatLegal; - } - - /// @return Always false; card nodes are not deck headers. - [[nodiscard]] bool isDeckHeader() const override - { - return false; - } - - /** - * @brief Convert this node to a CardRef. - * - * @return A CardRef with the card’s name and provider ID, suitable - * for database lookups or comparison with other card sources. - */ - [[nodiscard]] CardRef toCardRef() const - { - return {name, cardProviderId}; - } -}; - -#endif // COCKATRICE_DECK_LIST_CARD_NODE_H diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.cpp b/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.cpp deleted file mode 100644 index eca58963a..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "inner_deck_list_node.h" - -#include "deck_list_card_node.h" - -InnerDecklistNode::InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent) - : AbstractDecklistNode(_parent), name(other->getName()) -{ - for (int i = 0; i < other->size(); ++i) { - auto *inner = dynamic_cast(other->at(i)); - if (inner) { - new InnerDecklistNode(inner, this); - } else { - new DecklistCardNode(dynamic_cast(other->at(i)), this); - } - } -} - -InnerDecklistNode::~InnerDecklistNode() -{ - clearTree(); -} - -QString InnerDecklistNode::visibleNameFromName(const QString &_name) -{ - if (_name == DECK_ZONE_MAIN) { - return QObject::tr("Maindeck"); - } else if (_name == DECK_ZONE_SIDE) { - return QObject::tr("Sideboard"); - } else if (_name == DECK_ZONE_TOKENS) { - return QObject::tr("Tokens"); - } else { - return _name; - } -} - -void InnerDecklistNode::setSortMethod(DeckSortMethod method) -{ - sortMethod = method; - for (int i = 0; i < size(); i++) { - at(i)->setSortMethod(method); - } -} - -QString InnerDecklistNode::getVisibleName() const -{ - return visibleNameFromName(name); -} - -void InnerDecklistNode::clearTree() -{ - for (int i = 0; i < size(); i++) - delete at(i); - clear(); -} - -AbstractDecklistNode *InnerDecklistNode::findChild(const QString &_name) -{ - for (int i = 0; i < size(); i++) { - if (at(i)->getName() == _name) { - return at(i); - } - } - return nullptr; -} - -AbstractDecklistNode *InnerDecklistNode::findCardChildByNameProviderIdAndNumber(const QString &_name, - const QString &_providerId, - const QString &_cardNumber) -{ - for (const auto &i : *this) { - if (!i || i->getName() != _name) { - continue; - } - if (_cardNumber != "" && i->getCardCollectorNumber() != _cardNumber) { - continue; - } - if (_providerId != "" && i->getCardProviderId() != _providerId) { - continue; - } - return i; - } - return nullptr; -} - -int InnerDecklistNode::height() const -{ - return at(0)->height() + 1; -} - -int InnerDecklistNode::recursiveCount(bool countTotalCards) const -{ - int result = 0; - for (int i = 0; i < size(); i++) { - auto *node = dynamic_cast(at(i)); - - if (node) { - result += node->recursiveCount(countTotalCards); - } else if (countTotalCards) { - result += dynamic_cast(at(i))->getNumber(); - } else { - result++; - } - } - return result; -} - -bool InnerDecklistNode::compare(AbstractDecklistNode *other) const -{ - switch (sortMethod) { - case ByNumber: - return compareNumber(other); - case ByName: - return compareName(other); - default: - return false; - } -} - -bool InnerDecklistNode::compareNumber(AbstractDecklistNode *other) const -{ - auto *other2 = dynamic_cast(other); - if (other2) { - int n1 = recursiveCount(true); - int n2 = other2->recursiveCount(true); - return (n1 != n2) ? (n1 > n2) : compareName(other); - } else { - return false; - } -} - -bool InnerDecklistNode::compareName(AbstractDecklistNode *other) const -{ - auto *other2 = dynamic_cast(other); - if (other2) { - return (getName() > other2->getName()); - } else { - return false; - } -} - -bool InnerDecklistNode::readElement(QXmlStreamReader *xml) -{ - while (!xml->atEnd()) { - xml->readNext(); - const QString childName = xml->name().toString(); - if (xml->isStartElement()) { - if (childName == "zone") { - auto *newZone = new InnerDecklistNode(xml->attributes().value("name").toString(), this); - newZone->readElement(xml); - } else if (childName == "card") { - auto *newCard = new DecklistCardNode( - xml->attributes().value("name").toString(), xml->attributes().value("number").toString().toInt(), - this, -1, xml->attributes().value("setShortName").toString(), - xml->attributes().value("collectorNumber").toString(), xml->attributes().value("uuid").toString()); - newCard->readElement(xml); - } - } else if (xml->isEndElement() && (childName == "zone")) - return false; - } - return true; -} - -void InnerDecklistNode::writeElement(QXmlStreamWriter *xml) -{ - xml->writeStartElement("zone"); - xml->writeAttribute("name", name); - for (int i = 0; i < size(); i++) - at(i)->writeElement(xml); - xml->writeEndElement(); // zone -} - -QVector> InnerDecklistNode::sort(Qt::SortOrder order) -{ - QVector> result(size()); - - // Initialize temporary list with contents of current list - QVector> tempList(size()); - for (int i = size() - 1; i >= 0; --i) { - tempList[i].first = i; - tempList[i].second = at(i); - } - - // Sort temporary list - auto cmp = [order](const auto &a, const auto &b) { - return (order == Qt::AscendingOrder) ? (b.second->compare(a.second)) : (a.second->compare(b.second)); - }; - - std::sort(tempList.begin(), tempList.end(), cmp); - - // Map old indexes to new indexes and - // copy temporary list to the current one - for (int i = size() - 1; i >= 0; --i) { - result[i].first = tempList[i].first; - result[i].second = i; - replace(i, tempList[i].second); - } - - return result; -} \ No newline at end of file diff --git a/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.h b/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.h deleted file mode 100644 index f4c48afce..000000000 --- a/libcockatrice_deck_list/libcockatrice/deck_list/tree/inner_deck_list_node.h +++ /dev/null @@ -1,228 +0,0 @@ -/** - * @file inner_deck_list_node.h - * @brief Defines the InnerDecklistNode class, which represents - * structural nodes (zones and groups) in the deck tree. - * - * The deck tree consists of: - * - A root node (invisible). - * - Zones (Main, Sideboard, Tokens). - * - Optional grouping nodes (e.g., by type, color, or mana cost). - * - Card nodes as leaves. - * - * InnerDecklistNode implements the zone/group nodes and provides - * storage and management of child nodes. - */ - -#ifndef COCKATRICE_INNER_DECK_LIST_NODE_H -#define COCKATRICE_INNER_DECK_LIST_NODE_H - -#include "abstract_deck_list_node.h" - -/// Constant for the "main" deck zone name. -#define DECK_ZONE_MAIN "main" -/// Constant for the "sideboard" zone name. -#define DECK_ZONE_SIDE "side" -/// Constant for the "tokens" zone name. -#define DECK_ZONE_TOKENS "tokens" - -/** - * @class InnerDecklistNode - * @brief Represents a container node in the deck list hierarchy - * (zones and groupings). - * - * Unlike DecklistCardNode, which holds leaf card data, this class - * manages collections of child nodes, which may themselves be - * InnerDecklistNode or DecklistCardNode objects. - * - * ### Role in the hierarchy: - * - Root node (invisible): Holds zones. - * - Zone nodes: "main", "side", "tokens". - * - Grouping nodes: Created dynamically when grouping by type, - * color, or mana cost. - * - Card nodes: Always children of an InnerDecklistNode. - * - * ### Design notes: - * - Inherits from AbstractDecklistNode (tree interface) and - * QList (storage of children). - * This allows direct QList-style manipulation of children while - * still presenting a polymorphic node interface. - * - * ### Responsibilities: - * - Store a display name. - * - Own and manage child nodes (insert, clear, find). - * - Provide recursive operations such as counting cards or computing height. - * - Implement sorting logic for reordering children. - * - Implement XML serialization for persistence. - * - * ### Ownership: - * - Owns all child nodes stored in the QList. The destructor - * recursively deletes children. - */ -class InnerDecklistNode : public AbstractDecklistNode, public QList -{ - QString name; ///< Internal identifier for this node (zone or group name). - -public: - /** - * @brief Construct a new InnerDecklistNode. - * - * @param _name Internal name (e.g., "main", "side", "tokens", or group label). - * @param _parent Parent node (may be nullptr for the root). - * @param position Optional index for insertion into parent. -1 = append. - */ - explicit InnerDecklistNode(QString _name = QString(), InnerDecklistNode *_parent = nullptr, int position = -1) - : AbstractDecklistNode(_parent, position), name(std::move(_name)) - { - } - - /** - * @brief Copy constructor with parent reassignment. - * @param other Node to copy from (deep copy of children). - * @param _parent Parent node for the copy. - */ - explicit InnerDecklistNode(InnerDecklistNode *other, InnerDecklistNode *_parent = nullptr); - - /** - * @brief Destructor. Recursively deletes all child nodes. - */ - ~InnerDecklistNode() override; - - /** - * @brief Set the sorting method for this node and all children. - * @param method Sort method to apply recursively. - */ - void setSortMethod(DeckSortMethod method) override; - - /// @return The internal name of this node. - [[nodiscard]] QString getName() const override - { - return name; - } - - /// @param _name Set the internal name of this node. - void setName(const QString &_name) - { - name = _name; - } - - /** - * @brief Translate an internal name into a user-visible name. - * - * For example, the internal string "main" is presented as - * "Mainboard" in the UI. - * - * @param _name Internal identifier. - * @return Display-friendly string. - */ - static QString visibleNameFromName(const QString &_name); - - /** - * @brief Get this node’s display-friendly name. - * @return Human-readable name (zone/group name). - */ - [[nodiscard]] virtual QString getVisibleName() const; - - /// @return Always empty for container nodes. - [[nodiscard]] QString getCardProviderId() const override - { - return ""; - } - - /// @return Always empty for container nodes. - [[nodiscard]] QString getCardSetShortName() const override - { - return ""; - } - - /// @return Always empty for container nodes. - [[nodiscard]] QString getCardCollectorNumber() const override - { - return ""; - } - - /// @return Always true; InnerDecklistNode represents deck structure. - [[nodiscard]] bool isDeckHeader() const override - { - return true; - } - - /** - * @brief Delete all children of this node, recursively. - */ - void clearTree(); - - /** - * @brief Find a direct child node by name. - * @param _name Name to match. - * @return Pointer to child node, or nullptr if not found. - */ - AbstractDecklistNode *findChild(const QString &_name); - - /** - * @brief Find a child card node by name, provider ID, and collector number. - * - * Searches immediate children only. - * - * @param _name Card name to match. - * @param _providerId Optional provider ID to match. - * @param _cardNumber Optional collector number to match. - * @return Pointer to child node if found, nullptr otherwise. - */ - AbstractDecklistNode *findCardChildByNameProviderIdAndNumber(const QString &_name, - const QString &_providerId = "", - const QString &_cardNumber = ""); - - /** - * @brief Compute the height of this node. - * @return Maximum depth of descendants + 1. - */ - [[nodiscard]] int height() const override; - - /** - * @brief Count cards recursively under this node. - * @param countTotalCards If true, sums up quantities of cards. - * If false, counts unique card nodes. - * @return Total count. - */ - [[nodiscard]] int recursiveCount(bool countTotalCards = false) const; - - /** - * @brief Compare this node against another for sorting. - * - * Uses current @c sortMethod to determine the comparison. - * - * @param other Node to compare. - * @return true if this node should sort before @p other. - */ - bool compare(AbstractDecklistNode *other) const override; - - /// @copydoc compare(AbstractDecklistNode*) const - bool compareNumber(AbstractDecklistNode *other) const; - - /// @copydoc compare(AbstractDecklistNode*) const - bool compareName(AbstractDecklistNode *other) const; - - /** - * @brief Sort this node’s children recursively. - * - * @param order Ascending or descending. - * @return A QVector of (oldIndex, newIndex) pairs indicating - * how children were reordered. - */ - QVector> sort(Qt::SortOrder order = Qt::AscendingOrder); - - /** - * @brief Deserialize this node and its children from XML. - * @param xml Reader positioned at this element. - * @return true if parsing succeeded. - */ - bool readElement(QXmlStreamReader *xml) override; - - /** - * @brief Serialize this node and its children to XML. - * @param xml Writer to append elements to. - */ - void writeElement(QXmlStreamWriter *xml) override; -}; - -#endif // COCKATRICE_INNER_DECK_LIST_NODE_H diff --git a/libcockatrice_filters/CMakeLists.txt b/libcockatrice_filters/CMakeLists.txt deleted file mode 100644 index 74566ca05..000000000 --- a/libcockatrice_filters/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS libcockatrice/filters/filter_card.h libcockatrice/filters/filter_string.h - libcockatrice/filters/filter_tree.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_filters STATIC ${MOC_SOURCES} libcockatrice/filters/filter_card.cpp - libcockatrice/filters/filter_string.cpp libcockatrice/filters/filter_tree.cpp -) - -add_dependencies(libcockatrice_filters libcockatrice_card) - -target_include_directories(libcockatrice_filters PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_filters PUBLIC libcockatrice_card ${QT_CORE_MODULE}) diff --git a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp b/libcockatrice_filters/libcockatrice/filters/filter_card.cpp deleted file mode 100644 index 5fdce7ae0..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_card.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "filter_card.h" - -#include - -QJsonObject CardFilter::toJson() const -{ - QJsonObject obj; - obj["term"] = trm; - obj["type"] = typeName(t); - obj["attr"] = attrName(a); - return obj; -} - -CardFilter *CardFilter::fromJson(const QJsonObject &obj) -{ - QString term = obj["term"].toString(); - QString typeStr = obj["type"].toString(); - QString attrStr = obj["attr"].toString(); - - Type type = TypeEnd; - Attr attr = AttrEnd; - - // Convert type string back to enum - for (int i = 0; i < TypeEnd; i++) { - if (typeName(static_cast(i)) == typeStr) { - type = static_cast(i); - break; - } - } - - // Convert attr string back to enum - for (int i = 0; i < AttrEnd; i++) { - if (attrName(static_cast(i)) == attrStr) { - attr = static_cast(i); - break; - } - } - - return new CardFilter(term, type, attr); -} - -const QString CardFilter::typeName(Type t) -{ - switch (t) { - case TypeAnd: - return tr("AND", "Logical conjunction operator used in card filter"); - case TypeOr: - return tr("OR", "Logical disjunction operator used in card filter"); - case TypeAndNot: - return tr("AND NOT", "Negated logical conjunction operator used in card filter"); - case TypeOrNot: - return tr("OR NOT", "Negated logical disjunction operator used in card filter"); - default: - return QString(""); - } -} - -const QString CardFilter::attrName(Attr a) -{ - switch (a) { - case AttrName: - return tr("Name"); - case AttrNameExact: - return tr("Name (Exact)"); - case AttrType: - return tr("Type"); - case AttrColor: - return tr("Color"); - case AttrText: - return tr("Text"); - case AttrSet: - return tr("Set"); - case AttrManaCost: - return tr("Mana Cost"); - case AttrCmc: - return tr("Mana Value"); - case AttrRarity: - return tr("Rarity"); - case AttrPow: - return tr("Power"); - case AttrTough: - return tr("Toughness"); - case AttrLoyalty: - return tr("Loyalty"); - case AttrFormat: - return tr("Format"); - case AttrMainType: - return tr("Main Type"); - case AttrSubType: - return tr("Sub Type"); - default: - return QString(""); - } -} diff --git a/libcockatrice_filters/libcockatrice/filters/filter_card.h b/libcockatrice_filters/libcockatrice/filters/filter_card.h deleted file mode 100644 index 732e43a09..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_card.h +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file filter_card.h - * @ingroup CardDatabaseModelFilters - * @brief TODO: Document this. - */ - -#ifndef CARDFILTER_H -#define CARDFILTER_H - -#include -#include - -class CardFilter : public QObject -{ - Q_OBJECT - -public: - enum Type - { - TypeAnd = 0, - TypeOr, - TypeAndNot, - TypeOrNot, - TypeEnd - }; - - /* if you add an attribute here you also need to - * add its string representation in attrName */ - enum Attr - { - AttrCmc = 0, - AttrColor, - AttrLoyalty, - AttrManaCost, - AttrName, - AttrNameExact, - AttrPow, - AttrRarity, - AttrSet, - AttrText, - AttrTough, - AttrType, - AttrMainType, - AttrSubType, - AttrFormat, - AttrEnd, - }; - -private: - QString trm; - enum Type t; - enum Attr a; - -public: - CardFilter(QString &term, Type type, Attr attr) : trm(term), t(type), a(attr) - { - } - - [[nodiscard]] Type type() const - { - return t; - } - [[nodiscard]] const QString &term() const - { - return trm; - } - [[nodiscard]] Attr attr() const - { - return a; - } - - [[nodiscard]] QJsonObject toJson() const; - static CardFilter *fromJson(const QJsonObject &json); - static const QString typeName(Type t); - static const QString attrName(Attr a); -}; - -#endif diff --git a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp b/libcockatrice_filters/libcockatrice/filters/filter_string.cpp deleted file mode 100644 index 704e8fadb..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_string.cpp +++ /dev/null @@ -1,411 +0,0 @@ -#include "filter_string.h" - -#include -#include -#include -#include -#include -#include - -static peg::parser search(R"( -Start <- QueryPartList -~ws <- [ ]+ -QueryPartList <- ComplexQueryPart ( ws ("AND" ws)? ComplexQueryPart)* ws* - -ComplexQueryPart <- SomewhatComplexQueryPart ws "OR" ws ComplexQueryPart / SomewhatComplexQueryPart - -SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart - -QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery - -NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart -SetQuery <- ('e'/'set') [:] FlexStringValue -OracleQuery <- 'o' [:] MatcherString - - -CMCQuery <- ('cmc'/'mv') ws? NumericExpression -PowerQuery <- [Pp] 'ow' 'er'? ws? NumericExpression -ToughnessQuery <- [Tt] 'ou' 'ghness'? ws? NumericExpression - -RarityQuery <- [rR] ':' Rarity -Rarity <- [Cc] 'ommon'? / [Uu] 'ncommon'? / [Rr] 'are'? / [Mm] 'ythic'? / [Ss] 'pecial'? / [a-zA-Z] [a-z]* - -FormatQuery <- 'f' ':' Format / Legality ':' Format -Format <- [a-zA-Z] [a-z]* -Legality <- [Ll] 'egal'? / [Bb] 'anned'? / [Rr] 'estricted' - - -TypeQuery <- [tT] 'ype'? [:] StringValue - -Color <- < [Ww] 'hite'? / [Uu] / [Bb] 'lack'? / [Rr] 'ed'? / [Gg] 'reen'? / [Bb] 'lue'? > -ColorEx <- Color / [mc] - -ColorQuery <- [cC] 'olor'? <[iI]?> <[:!]> ColorEx* - -FieldQuery <- String [:] MatcherString / String ws? NumericExpression - -NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["]. -NonSingleQuoteUnlessEscaped <- "\\\'". / ![']. -UnescapedStringListPart <- !['":<>()=! ]. -SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)* -String <- SingleApostropheString / UnescapedStringListPart+ / ["] ["] / ['] ['] -StringValue <- String / [(] StringList [)] -StringList <- StringListString (ws? [,] ws? StringListString)* -StringListString <- UnescapedStringListPart+ -GenericQuery <- MatcherString - -# A String that can either be a normal string or a regex search string -MatcherString <- RegexMatcher / NormalMatcher - -NormalMatcher <- String -RegexMatcher <- '/' RegexMatcherString '/' -RegexMatcherString <- ('\\/' / !'/' .)+ - -FlexStringValue <- CompactStringSet / String / [(] StringList [)] -CompactStringSet <- StringListString ([,+] StringListString)+ - -NumericExpression <- NumericOperator ws? NumericValue -NumericOperator <- [=:] / <[> -NumericValue <- [0-9]+ -)"); - -static std::once_flag init; - -static void setupParserRules() -{ - auto passthru = [](const peg::SemanticValues &sv) -> Filter { - return !sv.empty() ? std::any_cast(sv[0]) : nullptr; - }; - - search["Start"] = passthru; - search["QueryPartList"] = [](const peg::SemanticValues &sv) -> Filter { - return [=](const CardData &x) { - auto matchesFilter = [&x](const std::any &query) { return std::any_cast(query)(x); }; - return std::all_of(sv.begin(), sv.end(), matchesFilter); - }; - }; - search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> Filter { - return [=](const CardData &x) { - auto matchesFilter = [&x](const std::any &query) { return std::any_cast(query)(x); }; - return std::any_of(sv.begin(), sv.end(), matchesFilter); - }; - }; - search["SomewhatComplexQueryPart"] = passthru; - search["QueryPart"] = passthru; - search["NotQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto dependent = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return !dependent(x); }; - }; - search["TypeQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return matcher(x->getCardType()); }; - }; - search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter { - auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { - QList sets = x->getSets().keys(); - - auto matchesSet = [&matcher](const QString &set) { return matcher(set); }; - return std::any_of(sets.begin(), sets.end(), matchesSet); - }; - }; - search["Rarity"] = [](const peg::SemanticValues &sv) -> QString { - switch (tolower(std::string(sv.sv())[0])) { - case 'c': - return "common"; - case 'u': - return "uncommon"; - case 'r': - return "rare"; - case 'm': - return "mythic"; - case 's': - return "special"; - default: - return QString::fromStdString(std::string(sv.sv())); - } - }; - search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto rarity = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { - QList infos; - for (const auto &printings : x->getSets()) { - for (const auto &printing : printings) { - infos.append(printing); - } - } - - auto matchesRarity = [&rarity](const PrintingInfo &info) { return rarity == info.getProperty("rarity"); }; - return std::any_of(infos.begin(), infos.end(), matchesRarity); - }; - }; - - search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter { - if (sv.choice() == 0) { - const auto format = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return x->getLegalityProp(format) == "legal"; }; - } - - const auto format = std::any_cast(sv[1]); - const auto legality = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return x->getLegalityProp(format) == legality; }; - }; - search["Legality"] = [](const peg::SemanticValues &sv) -> QString { - switch (tolower(std::string(sv.sv())[0])) { - case 'l': - return "legal"; - case 'b': - return "banned"; - case 'r': - return "restricted"; - default: - return ""; - } - }; - - search["Format"] = [](const peg::SemanticValues &sv) -> QString { - if (sv.size() == 1) { - switch (tolower(std::string(sv.sv())[0])) { - case 'm': - return "modern"; - case 's': - return "standard"; - case 'v': - return "vintage"; - case 'l': - return "legacy"; - case 'c': - return "commander"; - case 'p': - return "pioneer"; - default: - return ""; - } - } - - return QString::fromStdString(std::string(sv.sv())).toLower(); - }; - - search["StringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher { - // Helper function for word boundary matching - auto createWordBoundaryMatcher = [](const QString &target) { - QString pattern = QString("\\b%1\\b").arg(QRegularExpression::escape(target)); - QRegularExpression regex(pattern, QRegularExpression::CaseInsensitiveOption); - return [regex](const QString &s) { return regex.match(s).hasMatch(); }; - }; - - if (sv.choice() == 0) { - const auto target = std::any_cast(sv[0]); - return createWordBoundaryMatcher(target); - } - - const auto target = std::any_cast(sv[0]); - return [=](const QString &s) { - auto containsString = [&s, &createWordBoundaryMatcher](const QString &str) { - return createWordBoundaryMatcher(str)(s); - }; - return std::any_of(target.begin(), target.end(), containsString); - }; - }; - - search["String"] = [](const peg::SemanticValues &sv) -> QString { - if (sv.choice() == 0) { - return QString::fromStdString(std::string(sv.sv())); - } - - return QString::fromStdString(std::string(sv.token(0))); - }; - search["FlexStringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher { - if (sv.choice() != 1) { - const auto target = std::any_cast(sv[0]); - return [=](const QString &s) { - auto containsString = [&s](const QString &str) { - return s.split(" ").contains(str, Qt::CaseInsensitive); - }; - return std::any_of(target.begin(), target.end(), containsString); - }; - } - - const auto target = std::any_cast(sv[0]); - return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); }; - }; - search["CompactStringSet"] = [](const peg::SemanticValues &sv) -> QStringList { - QStringList result; - for (const auto &i : sv) { - result.append(std::any_cast(i)); - } - return result; - }; - search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList { - QStringList result; - for (const auto &i : sv) { - result.append(std::any_cast(i)); - } - return result; - }; - search["StringListString"] = [](const peg::SemanticValues &sv) -> QString { - return QString::fromStdString(std::string(sv.sv())); - }; - - search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher { - const auto arg = std::any_cast(sv[1]); - const auto op = std::any_cast(sv[0]); - - if (op == ">") - return [=](const int s) { return s > arg; }; - if (op == ">=") - return [=](const int s) { return s >= arg; }; - if (op == "<") - return [=](const int s) { return s < arg; }; - if (op == "<=") - return [=](const int s) { return s <= arg; }; - if (op == "=") - return [=](const int s) { return s == arg; }; - if (op == ":") - return [=](const int s) { return s == arg; }; - if (op == "!=") - return [=](const int s) { return s != arg; }; - return [](int) { return false; }; - }; - - search["NumericValue"] = [](const peg::SemanticValues &sv) -> int { - return QString::fromStdString(std::string(sv.sv())).toInt(); - }; - - search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString { - return QString::fromStdString(std::string(sv.sv())); - }; - - search["NormalMatcher"] = [](const peg::SemanticValues &sv) -> StringMatcher { - auto target = std::any_cast(sv[0]); - auto sanitizedTarget = QString(target); - sanitizedTarget.replace("\\\"", "\""); - sanitizedTarget.replace("\\'", "'"); - return [=](const QString &s) { return s.contains(sanitizedTarget, Qt::CaseInsensitive); }; - }; - - search["RegexMatcher"] = [](const peg::SemanticValues &sv) -> StringMatcher { - auto target = std::any_cast(sv[0]); - auto regex = QRegularExpression(target, QRegularExpression::CaseInsensitiveOption); - return [=](const QString &s) { return regex.match(s).hasMatch(); }; - }; - - search["RegexMatcherString"] = [](const peg::SemanticValues &sv) -> QString { - return QString::fromStdString(sv.token_to_string()); - }; - - search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) { return matcher(x->getText()); }; - }; - - search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter { - QString parts; - for (const auto &i : sv) { - parts += std::any_cast(i); - } - const bool identity = sv.tokens[0].empty() || sv.tokens[0][0] != 'i'; - if (sv.tokens[1][0] == ':') { - return [=](const CardData &x) { - QString match = identity ? x->getColors() : x->getProperty("coloridentity"); - if (parts.contains("m") && match.length() < 2) { - return false; - } - if (parts == "m") { - return true; - } - - if (parts.contains("c") && match.length() == 0) - return true; - - auto containsColor = [&parts](const QString &s) { return parts.contains(s); }; - return std::any_of(match.begin(), match.end(), containsColor); - }; - } - - return [=](const CardData &x) { - QString match = identity ? x->getColors() : x->getProperty("colorIdentity"); - if (parts.contains("m") && match.length() < 2) { - return false; - } - - if (parts.contains("c") && match.length() != 0) { - return false; - } - - for (const auto &part : parts) { - if (!match.contains(part)) { - return false; - } - } - - auto containsColor = [&parts](const QString &s) { return parts.contains(s); }; - return std::all_of(match.begin(), match.end(), containsColor); - }; - }; - - search["CMCQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return matcher(x->getProperty("cmc").toInt()); }; - }; - search["PowerQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); }; - }; - search["ToughnessQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) -> bool { - auto parts = x->getPowTough().split("/"); - return matcher(parts.length() == 2 ? parts[1].toInt() : 0); - }; - }; - search["FieldQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto field = std::any_cast(sv[0]); - if (sv.choice() == 0) { - const auto matcher = std::any_cast(sv[1]); - return [=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field)); }; - } - - const auto matcher = std::any_cast(sv[1]); - return - [=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field).toInt()); }; - }; - search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter { - const auto matcher = std::any_cast(sv[0]); - return [=](const CardData &x) { return matcher(x->getName()); }; - }; - - search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; }; - search["ColorEx"] = [](const peg::SemanticValues &sv) -> char { - return sv.choice() == 0 ? std::any_cast(sv[0]) : *std::string(sv.sv()).c_str(); - }; -} - -FilterString::FilterString() -{ - result = [](const CardData &) -> bool { return false; }; - _error = "Not initialized"; -} - -FilterString::FilterString(const QString &expr) -{ - QByteArray ba = expr.simplified().toUtf8(); - - std::call_once(init, setupParserRules); - - _error = QString(); - - if (ba.isEmpty()) { - result = [](const CardData &) -> bool { return true; }; - return; - } - - search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) { - _error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg)); - }); - - if (!search.parse(ba.data(), result)) { - qCInfo(FilterStringLog).nospace() << "FilterString error for " << expr << "; " << qPrintable(_error); - result = [](const CardData &) -> bool { return false; }; - } -} diff --git a/libcockatrice_filters/libcockatrice/filters/filter_string.h b/libcockatrice_filters/libcockatrice/filters/filter_string.h deleted file mode 100644 index 8fc41ce68..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_string.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file filter_string.h - * @ingroup CardDatabaseModelFilters - * @brief TODO: Document this. - */ - -#ifndef FILTER_STRING_H -#define FILTER_STRING_H - -#include "filter_tree.h" - -#include -#include -#include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(FilterStringLog, "filter_string"); - -typedef CardInfoPtr CardData; -typedef std::function Filter; -typedef std::function StringMatcher; -typedef std::function NumberMatcher; - -namespace peg -{ -template struct AstBase; -struct EmptyType; -typedef AstBase Ast; -} // namespace peg - -class FilterString -{ -public: - FilterString(); - explicit FilterString(const QString &exp); - [[nodiscard]] bool check(const CardData &card) const - { - if (card.isNull()) { - static CardInfoPtr blankCard = CardInfo::newInstance(""); - return result(blankCard); - } - return result(card); - } - - bool valid() - { - return _error.isEmpty(); - } - - QString error() - { - return _error; - } - -private: - QString _error; - Filter result; -}; - -#endif diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp b/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp deleted file mode 100644 index 19e8c2d8d..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.cpp +++ /dev/null @@ -1,572 +0,0 @@ -#include "filter_tree.h" - -#include "filter_card.h" - -#include - -template FilterTreeNode *FilterTreeBranch::nodeAt(int i) const -{ - return (childNodes.size() > i) ? childNodes.at(i) : nullptr; -} - -template void FilterTreeBranch::deleteAt(int i) -{ - preRemoveChild(this, i); - delete childNodes.takeAt(i); - postRemoveChild(this, i); - nodeChanged(); -} - -template int FilterTreeBranch::childIndex(const FilterTreeNode *node) const -{ - auto *unconst = const_cast(node); - auto downcasted = dynamic_cast(unconst); - return (downcasted) ? childNodes.indexOf(downcasted) : -1; -} - -template FilterTreeBranch::~FilterTreeBranch() -{ - while (!childNodes.isEmpty()) { - delete childNodes.takeFirst(); - } -} - -const FilterItemList *LogicMap::findTypeList(CardFilter::Type type) const -{ - QList::const_iterator i; - - for (i = childNodes.constBegin(); i != childNodes.constEnd(); ++i) { - if ((*i)->type == type) { - return *i; - } - } - - return nullptr; -} - -FilterItemList *LogicMap::typeList(CardFilter::Type type) -{ - QList::iterator i; - int count = 0; - - for (i = childNodes.begin(); i != childNodes.end(); ++i) { - if ((*i)->type == type) { - break; - } - count++; - } - - if (i == childNodes.end()) { - preInsertChild(this, count); - i = childNodes.insert(i, new FilterItemList(type, this)); - postInsertChild(this, count); - nodeChanged(); - } - - return *i; -} - -FilterTreeNode *LogicMap::parent() const -{ - return p; -} - -int FilterItemList::termIndex(const QString &term) const -{ - for (int i = 0; i < childNodes.count(); i++) { - if ((childNodes.at(i))->term == term) { - return i; - } - } - - return -1; -} - -FilterTreeNode *FilterItemList::termNode(const QString &term) -{ - int i = termIndex(term); - if (i < 0) { - FilterItem *fi = new FilterItem(term, this); - int count = childNodes.count(); - - preInsertChild(this, count); - childNodes.append(fi); - postInsertChild(this, count); - nodeChanged(); - - return fi; - } - - return childNodes.at(i); -} - -bool FilterItemList::testTypeAnd(const CardInfoPtr info, CardFilter::Attr attr) const -{ - for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { - if (!(*i)->isEnabled()) { - continue; - } - - if (!(*i)->acceptCardAttr(info, attr)) { - return false; - } - } - - return true; -} - -bool FilterItemList::testTypeAndNot(const CardInfoPtr info, CardFilter::Attr attr) const -{ - // if any one in the list is true, return false - return !testTypeOr(info, attr); -} - -bool FilterItemList::testTypeOr(const CardInfoPtr info, CardFilter::Attr attr) const -{ - bool noChildEnabledChild = true; - - for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { - if (!(*i)->isEnabled()) { - continue; - } - - if (noChildEnabledChild) { - noChildEnabledChild = false; - } - - if ((*i)->acceptCardAttr(info, attr)) { - return true; - } - } - - return noChildEnabledChild; -} - -bool FilterItemList::testTypeOrNot(const CardInfoPtr info, CardFilter::Attr attr) const -{ - // if any one in the list is false, return true - return !testTypeAnd(info, attr); -} - -bool FilterItem::acceptName(const CardInfoPtr info) const -{ - return info->getName().contains(term, Qt::CaseInsensitive); -} - -bool FilterItem::acceptNameExact(const CardInfoPtr info) const -{ - return info->getName() == term; -} - -bool FilterItem::acceptType(const CardInfoPtr info) const -{ - return info->getCardType().contains(term, Qt::CaseInsensitive); -} - -bool FilterItem::acceptMainType(const CardInfoPtr info) const -{ - const QStringList typeParts = info->getCardType().split(" — "); - return !typeParts.isEmpty() && typeParts[0].contains(term, Qt::CaseInsensitive); -} - -bool FilterItem::acceptSubType(const CardInfoPtr info) const -{ - const QStringList typeParts = info->getCardType().split(" — "); - return typeParts.size() > 1 && typeParts[1].contains(term, Qt::CaseInsensitive); -} - -bool FilterItem::acceptColor(const CardInfoPtr info) const -{ - QString converted_term = term.trimmed(); - - converted_term.replace("green", "g", Qt::CaseInsensitive); - converted_term.replace("grn", "g", Qt::CaseInsensitive); - converted_term.replace("blue", "u", Qt::CaseInsensitive); - converted_term.replace("blu", "u", Qt::CaseInsensitive); - converted_term.replace("black", "b", Qt::CaseInsensitive); - converted_term.replace("blk", "b", Qt::CaseInsensitive); - converted_term.replace("red", "r", Qt::CaseInsensitive); - converted_term.replace("white", "w", Qt::CaseInsensitive); - converted_term.replace("wht", "w", Qt::CaseInsensitive); - converted_term.replace("colorless", "c", Qt::CaseInsensitive); - converted_term.replace("colourless", "c", Qt::CaseInsensitive); - converted_term.replace("none", "c", Qt::CaseInsensitive); - - converted_term.replace(QString(" "), QString(""), Qt::CaseInsensitive); - - // Colorless card filter - if (converted_term.toLower() == "c" && info->getColors().length() < 1) { - return true; - } - - /* - * This is a tricky part, if the filter has multiple colors in it, like UGW, - * then we should match all of them to the card's colors - */ - int match_count = 0; - for (auto &it : converted_term) { - if (info->getColors().contains(it, Qt::CaseInsensitive)) - match_count++; - } - - return match_count == converted_term.length(); -} - -bool FilterItem::acceptText(const CardInfoPtr info) const -{ - return info->getText().contains(term, Qt::CaseInsensitive); -} - -bool FilterItem::acceptSet(const CardInfoPtr info) const -{ - bool status = false; - for (const auto &printings : info->getSets()) { - for (const auto &set : printings) { - if (set.getSet()->getShortName().compare(term, Qt::CaseInsensitive) == 0 || - set.getSet()->getLongName().compare(term, Qt::CaseInsensitive) == 0) { - status = true; - break; - } - } - } - - return status; -} - -bool FilterItem::acceptManaCost(const CardInfoPtr info) const -{ - QString partialCost = term.toUpper(); - - // Sort the mana cost so it will be easy to find - std::sort(partialCost.begin(), partialCost.end()); - - // Try to seperate the mana cost in case it's a split card - // if it's not a split card the loop will run only once - for (QString fullManaCost : info->getManaCost().split("//")) { - std::sort(fullManaCost.begin(), fullManaCost.end()); - - // If the partial is found in the full, return true - if (fullManaCost.contains(partialCost)) { - return true; - } - } - return false; -} - -bool FilterItem::acceptCmc(const CardInfoPtr info) const -{ - bool convertSuccess; - int cmcInt = info->getCmc().toInt(&convertSuccess); - // if conversion failed, check for the "//" separator used in split cards - if (!convertSuccess) { - int cmcSum = 0; - for (const QString &cmc : info->getCmc().split("//")) { - cmcInt = cmc.toInt(); - cmcSum += cmcInt; - if (relationCheck(cmcInt)) { - return true; - } - } - return relationCheck(cmcSum); - } else { - return relationCheck(cmcInt); - } -} - -bool FilterItem::acceptFormat(const CardInfoPtr info) const -{ - return info->getLegalityProp(term.toLower()) == "legal"; -} - -bool FilterItem::acceptLoyalty(const CardInfoPtr info) const -{ - if (info->getLoyalty().isEmpty()) { - return false; - } else { - bool success; - // if loyalty can't be converted to "int" it must be "X" - int loyalty = info->getLoyalty().toInt(&success); - if (success) { - return relationCheck(loyalty); - } else { - return term.trimmed().toUpper() == info->getLoyalty(); - } - } -} - -bool FilterItem::acceptPowerToughness(const CardInfoPtr info, CardFilter::Attr attr) const -{ - int slash = info->getPowTough().indexOf("/"); - if (slash == -1) { - return false; - } - QString valueString; - if (attr == CardFilter::AttrPow) { - valueString = info->getPowTough().mid(0, slash); - } else { - valueString = info->getPowTough().mid(slash + 1); - } - if (term == valueString) { - return true; - } - // advanced filtering should only happen after fast string comparison failed - bool conversion; - int value = valueString.toInt(&conversion); - return conversion ? relationCheck(value) : false; -} - -bool FilterItem::acceptRarity(const CardInfoPtr info) const -{ - QString converted_term = term.trimmed(); - - /* - * The purpose of this loop is to only apply one of the replacement - * policies and then escape. If we attempt to layer them on top of - * each other, we will get awkward results (i.e. comythic rare mythic rareon) - * Conditional statement will exit once a case is successful in - * replacement OR we go through all possible cases. - * Will also need to replace just "mythic" - */ - for (int i = 0; converted_term.length() <= 3 && i <= 6; i++) { - switch (i) { - case 0: - converted_term.replace("mr", "mythic", Qt::CaseInsensitive); - break; - case 1: - converted_term.replace("m r", "mythic", Qt::CaseInsensitive); - break; - case 2: - converted_term.replace("m", "mythic", Qt::CaseInsensitive); - break; - case 3: - converted_term.replace("c", "common", Qt::CaseInsensitive); - break; - case 4: - converted_term.replace("u", "uncommon", Qt::CaseInsensitive); - break; - case 5: - converted_term.replace("r", "rare", Qt::CaseInsensitive); - break; - case 6: - converted_term.replace("s", "special", Qt::CaseInsensitive); - break; - default: - break; - } - } - - for (const auto &printings : info->getSets()) { - for (const auto &printing : printings) { - if (printing.getProperty("rarity").compare(converted_term, Qt::CaseInsensitive) == 0) { - return true; - } - } - } - return false; -} - -bool FilterItem::relationCheck(int cardInfo) const -{ - bool result, conversion; - - // if int conversion fails, there's probably an operator at the start - result = (cardInfo == term.toInt(&conversion)); - if (!conversion) { - // leading whitespaces could cause indexing to fail - QString trimmedTerm = term.trimmed(); - // check whether it's a 2 char operator (<=, >=, or ==) - if (trimmedTerm[1] == '=') { - int termInt = trimmedTerm.mid(2).toInt(); - if (trimmedTerm.startsWith('<')) { - result = (cardInfo <= termInt); - } else if (trimmedTerm.startsWith('>')) { - result = (cardInfo >= termInt); - } else { - result = (cardInfo == termInt); - } - } else { - int termInt = trimmedTerm.mid(1).toInt(); - if (trimmedTerm.startsWith('<')) { - result = (cardInfo < termInt); - } else if (trimmedTerm.startsWith('>')) { - result = (cardInfo > termInt); - } else if (trimmedTerm.startsWith("=")) { - result = (cardInfo == termInt); - } else { - // the int conversion hasn't failed due to an operator at the start - result = false; - } - } - } - return result; -} - -bool FilterItem::acceptCardAttr(const CardInfoPtr info, CardFilter::Attr attr) const -{ - switch (attr) { - case CardFilter::AttrName: - return acceptName(info); - case CardFilter::AttrNameExact: - return acceptNameExact(info); - case CardFilter::AttrType: - return acceptType(info); - case CardFilter::AttrColor: - return acceptColor(info); - case CardFilter::AttrText: - return acceptText(info); - case CardFilter::AttrSet: - return acceptSet(info); - case CardFilter::AttrManaCost: - return acceptManaCost(info); - case CardFilter::AttrCmc: - return acceptCmc(info); - case CardFilter::AttrRarity: - return acceptRarity(info); - case CardFilter::AttrPow: - case CardFilter::AttrTough: - // intentional fallthrough - return acceptPowerToughness(info, attr); - case CardFilter::AttrLoyalty: - return acceptLoyalty(info); - case CardFilter::AttrFormat: - return acceptFormat(info); - case CardFilter::AttrMainType: - return acceptMainType(info); - case CardFilter::AttrSubType: - return acceptSubType(info); - default: - return true; /* ignore this attribute */ - } -} - -/* - * Need to define these here to make QT happy, otherwise - * moc doesnt find some of the FilterTreeBranch symbols. - */ -FilterTree::FilterTree() = default; -FilterTree::~FilterTree() = default; - -LogicMap *FilterTree::attrLogicMap(CardFilter::Attr attr) -{ - QList::iterator i; - - int count = 0; - for (i = childNodes.begin(); i != childNodes.end(); i++) { - if ((*i)->attr == attr) { - break; - } - count++; - } - - if (i == childNodes.end()) { - preInsertChild(this, count); - i = childNodes.insert(i, new LogicMap(attr, this)); - postInsertChild(this, count); - nodeChanged(); - } - - return *i; -} - -FilterItemList *FilterTree::attrTypeList(CardFilter::Attr attr, CardFilter::Type type) -{ - return attrLogicMap(attr)->typeList(type); -} - -FilterTreeNode *FilterTree::termNode(CardFilter::Attr attr, CardFilter::Type type, const QString &term) -{ - return attrTypeList(attr, type)->termNode(term); -} - -FilterTreeNode *FilterTree::termNode(const CardFilter *f) -{ - return termNode(f->attr(), f->type(), f->term()); -} - -bool FilterTree::testAttr(const CardInfoPtr info, const LogicMap *lm) const -{ - const FilterItemList *fil; - bool status = true; - - fil = lm->findTypeList(CardFilter::TypeAnd); - if (fil && fil->isEnabled() && !fil->testTypeAnd(info, lm->attr)) { - return false; - } - - fil = lm->findTypeList(CardFilter::TypeAndNot); - if (fil && fil->isEnabled() && !fil->testTypeAndNot(info, lm->attr)) { - return false; - } - - fil = lm->findTypeList(CardFilter::TypeOr); - if (fil && fil->isEnabled()) { - status = false; - - // if this is true we can return because it is OR'd with the OrNot list - if (fil->testTypeOr(info, lm->attr)) { - return true; - } - } - - fil = lm->findTypeList(CardFilter::TypeOrNot); - if (fil && fil->isEnabled() && fil->testTypeOrNot(info, lm->attr)) { - return true; - } - - return status; -} - -bool FilterTree::acceptsCard(const CardInfoPtr info) const -{ - for (auto i = childNodes.constBegin(); i != childNodes.constEnd(); i++) { - if ((*i)->isEnabled() && !testAttr(info, *i)) { - return false; - } - } - - return true; -} - -void FilterTree::removeFiltersByAttr(CardFilter::Attr filterType) -{ - for (int i = childNodes.size() - 1; i >= 0; --i) { - auto *child = dynamic_cast(childNodes.at(i)); - - if (child && child->attr == filterType) { - deleteAt(i); - } - } -} - -void FilterTree::removeFilter(const CardFilter *toRemove) -{ - for (int i = childNodes.size() - 1; i >= 0; --i) { - auto *logicMap = dynamic_cast(childNodes.at(i)); - if (!logicMap || logicMap->attr != toRemove->attr()) - continue; - - FilterItemList *typeList = logicMap->typeList(toRemove->type()); - if (!typeList) - continue; - - int termIdx = typeList->termIndex(toRemove->term()); - if (termIdx != -1) { - typeList->deleteAt(termIdx); - emit typeList->nodeChanged(); - if (typeList->childCount() == 0) { - int logicIndex = logicMap->childIndex(typeList); - if (logicIndex != -1) { - logicMap->deleteAt(logicIndex); - } - } - } - } -} - -void FilterTree::clear() -{ - while (childCount() > 0) { - deleteAt(0); - } - emit changed(); -} diff --git a/libcockatrice_filters/libcockatrice/filters/filter_tree.h b/libcockatrice_filters/libcockatrice/filters/filter_tree.h deleted file mode 100644 index 75d4241a5..000000000 --- a/libcockatrice_filters/libcockatrice/filters/filter_tree.h +++ /dev/null @@ -1,283 +0,0 @@ -/** - * @file filter_tree.h - * @ingroup CardDatabaseModelFilters - * @brief TODO: Document this. - */ - -#ifndef FILTERTREE_H -#define FILTERTREE_H - -#include "filter_card.h" - -#include -#include -#include -#include - -class FilterTreeNode -{ -private: - bool enabled; - -public: - FilterTreeNode() : enabled(true) - { - } - [[nodiscard]] virtual bool isEnabled() const - { - return enabled; - } - virtual void enable() - { - enabled = true; - nodeChanged(); - } - virtual void disable() - { - enabled = false; - nodeChanged(); - } - [[nodiscard]] virtual FilterTreeNode *parent() const - { - return nullptr; - } - [[nodiscard]] virtual FilterTreeNode *nodeAt(int /* i */) const - { - return nullptr; - } - virtual void deleteAt(int /* i */) - { - } - [[nodiscard]] virtual int childCount() const - { - return 0; - } - virtual int childIndex(const FilterTreeNode * /* node */) const - { - return -1; - } - [[nodiscard]] virtual int index() const - { - return (parent() != nullptr) ? parent()->childIndex(this) : -1; - } - [[nodiscard]] virtual const QString text() const - { - return QString(""); - } - [[nodiscard]] virtual bool isLeaf() const - { - return false; - } - virtual void nodeChanged() const - { - if (parent() != nullptr) - parent()->nodeChanged(); - } - virtual void preInsertChild(const FilterTreeNode *p, int i) const - { - if (parent() != nullptr) - parent()->preInsertChild(p, i); - } - virtual void postInsertChild(const FilterTreeNode *p, int i) const - { - if (parent() != nullptr) - parent()->postInsertChild(p, i); - } - virtual void preRemoveChild(const FilterTreeNode *p, int i) const - { - if (parent() != nullptr) - parent()->preRemoveChild(p, i); - } - virtual void postRemoveChild(const FilterTreeNode *p, int i) const - { - if (parent() != nullptr) - parent()->postRemoveChild(p, i); - } -}; - -template class FilterTreeBranch : public FilterTreeNode -{ -protected: - QList childNodes; - -public: - virtual ~FilterTreeBranch(); - void removeFiltersByAttr(CardFilter::Attr filterType); - [[nodiscard]] FilterTreeNode *nodeAt(int i) const override; - void deleteAt(int i) override; - [[nodiscard]] int childCount() const override - { - return childNodes.size(); - } - int childIndex(const FilterTreeNode *node) const override; -}; - -class FilterItemList; -class FilterTree; -class LogicMap : public FilterTreeBranch -{ - -private: - FilterTree *const p; - -public: - const CardFilter::Attr attr; - - LogicMap(CardFilter::Attr a, FilterTree *parent) : p(parent), attr(a) - { - } - [[nodiscard]] const FilterItemList *findTypeList(CardFilter::Type type) const; - FilterItemList *typeList(CardFilter::Type type); - [[nodiscard]] FilterTreeNode *parent() const override; - [[nodiscard]] const QString text() const override - { - return CardFilter::attrName(attr); - } -}; - -class FilterItem; -class FilterItemList : public FilterTreeBranch -{ -private: - LogicMap *const p; - -public: - const CardFilter::Type type; - - FilterItemList(CardFilter::Type t, LogicMap *parent) : p(parent), type(t) - { - } - [[nodiscard]] CardFilter::Attr attr() const - { - return p->attr; - } - [[nodiscard]] FilterTreeNode *parent() const override - { - return p; - } - [[nodiscard]] int termIndex(const QString &term) const; - FilterTreeNode *termNode(const QString &term); - [[nodiscard]] const QString text() const override - { - return CardFilter::typeName(type); - } - - [[nodiscard]] bool testTypeAnd(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeAndNot(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeOr(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool testTypeOrNot(CardInfoPtr info, CardFilter::Attr attr) const; -}; - -class FilterItem : public FilterTreeNode -{ -private: - FilterItemList *const p; - -public: - const QString term; - - FilterItem(QString trm, FilterItemList *parent) : p(parent), term(std::move(trm)) - { - } - virtual ~FilterItem() = default; - - [[nodiscard]] CardFilter::Attr attr() const - { - return p->attr(); - } - [[nodiscard]] CardFilter::Type type() const - { - return p->type; - } - [[nodiscard]] FilterTreeNode *parent() const override - { - return p; - } - [[nodiscard]] const QString text() const override - { - return term; - } - [[nodiscard]] bool isLeaf() const override - { - return true; - } - - [[nodiscard]] bool acceptName(CardInfoPtr info) const; - [[nodiscard]] bool acceptNameExact(CardInfoPtr info) const; - [[nodiscard]] bool acceptType(CardInfoPtr info) const; - [[nodiscard]] bool acceptMainType(CardInfoPtr info) const; - [[nodiscard]] bool acceptSubType(CardInfoPtr info) const; - [[nodiscard]] bool acceptColor(CardInfoPtr info) const; - [[nodiscard]] bool acceptText(CardInfoPtr info) const; - [[nodiscard]] bool acceptSet(CardInfoPtr info) const; - [[nodiscard]] bool acceptManaCost(CardInfoPtr info) const; - [[nodiscard]] bool acceptCmc(CardInfoPtr info) const; - [[nodiscard]] bool acceptPowerToughness(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool acceptLoyalty(CardInfoPtr info) const; - [[nodiscard]] bool acceptRarity(CardInfoPtr info) const; - [[nodiscard]] bool acceptCardAttr(CardInfoPtr info, CardFilter::Attr attr) const; - [[nodiscard]] bool acceptFormat(CardInfoPtr info) const; - [[nodiscard]] bool relationCheck(int cardInfo) const; -}; - -class FilterTree : public QObject, public FilterTreeBranch -{ - Q_OBJECT - -signals: - void preInsertRow(const FilterTreeNode *parent, int i) const; - void postInsertRow(const FilterTreeNode *parent, int i) const; - void preRemoveRow(const FilterTreeNode *parent, int i) const; - void postRemoveRow(const FilterTreeNode *parent, int i) const; - void changed() const; - -private: - LogicMap *attrLogicMap(CardFilter::Attr attr); - FilterItemList *attrTypeList(CardFilter::Attr attr, CardFilter::Type type); - - bool testAttr(CardInfoPtr info, const LogicMap *lm) const; - - void nodeChanged() const override - { - emit changed(); - } - void preInsertChild(const FilterTreeNode *p, int i) const override - { - emit preInsertRow(p, i); - } - void postInsertChild(const FilterTreeNode *p, int i) const override - { - emit postInsertRow(p, i); - } - void preRemoveChild(const FilterTreeNode *p, int i) const override - { - emit preRemoveRow(p, i); - } - void postRemoveChild(const FilterTreeNode *p, int i) const override - { - emit postRemoveRow(p, i); - } - -public: - FilterTree(); - ~FilterTree() override; - - FilterTreeNode *termNode(CardFilter::Attr attr, CardFilter::Type type, const QString &term); - FilterTreeNode *termNode(const CardFilter *f); - - [[nodiscard]] const QString text() const override - { - return QString("root"); - } - [[nodiscard]] int index() const override - { - return 0; - } - - [[nodiscard]] bool acceptsCard(CardInfoPtr info) const; - void removeFiltersByAttr(CardFilter::Attr filterType); - void removeFilter(const CardFilter *toRemove); - void clear(); -}; - -#endif diff --git a/libcockatrice_interfaces/CMakeLists.txt b/libcockatrice_interfaces/CMakeLists.txt deleted file mode 100644 index 4f34f7985..000000000 --- a/libcockatrice_interfaces/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - libcockatrice/interfaces/interface_card_database_path_provider.h - libcockatrice/interfaces/interface_card_preference_provider.h - libcockatrice/interfaces/interface_card_set_priority_controller.h - libcockatrice/interfaces/interface_network_settings_provider.h - libcockatrice/interfaces/noop_card_database_path_provider.h - libcockatrice/interfaces/noop_card_preference_provider.h - libcockatrice/interfaces/noop_card_set_priority_controller.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library(libcockatrice_interfaces STATIC ${MOC_SOURCES}) - -target_include_directories(libcockatrice_interfaces PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_interfaces PUBLIC ${QT_CORE_MODULE}) diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_database_path_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_database_path_provider.h deleted file mode 100644 index dd55ab789..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_database_path_provider.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef COCKATRICE_INTERFACE_CARD_DATABASE_PATH_PROVIDER_H -#define COCKATRICE_INTERFACE_CARD_DATABASE_PATH_PROVIDER_H -#include - -class ICardDatabasePathProvider : public QObject -{ - Q_OBJECT - -public: - virtual ~ICardDatabasePathProvider() = default; - - [[nodiscard]] virtual QString getCardDatabasePath() const = 0; - [[nodiscard]] virtual QString getCustomCardDatabasePath() const = 0; - [[nodiscard]] virtual QString getTokenDatabasePath() const = 0; - [[nodiscard]] virtual QString getSpoilerCardDatabasePath() const = 0; - -signals: - void cardDatabasePathChanged(); -}; - -#endif // COCKATRICE_INTERFACE_CARD_DATABASE_PATH_PROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_preference_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_preference_provider.h deleted file mode 100644 index a6cf941fb..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_preference_provider.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef COCKATRICE_INTERFACE_CARD_PREFERENCE_PROVIDER_H -#define COCKATRICE_INTERFACE_CARD_PREFERENCE_PROVIDER_H - -#include - -class ICardPreferenceProvider -{ -public: - virtual ~ICardPreferenceProvider() = default; - [[nodiscard]] virtual QString getCardPreferenceOverride(const QString &cardName) const = 0; - [[nodiscard]] virtual bool getIncludeRebalancedCards() const = 0; -}; - -#endif // COCKATRICE_INTERFACE_CARD_PREFERENCE_PROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h deleted file mode 100644 index b8fbbc74a..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_card_set_priority_controller.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H -#define COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H - -#include - -class ICardSetPriorityController -{ -public: - virtual ~ICardSetPriorityController() = default; - - virtual void setSortKey(QString shortName, unsigned int sortKey) = 0; - virtual void setEnabled(QString shortName, bool enabled) = 0; - virtual void setIsKnown(QString shortName, bool isknown) = 0; - - virtual unsigned int getSortKey(QString shortName) const = 0; - virtual bool isEnabled(QString shortName) const = 0; - virtual bool isKnown(QString shortName) const = 0; -}; - -#endif // COCKATRICE_INTERFACE_CARD_SET_PRIORITY_CONTROLLER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/interface_network_settings_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/interface_network_settings_provider.h deleted file mode 100644 index bd136513d..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/interface_network_settings_provider.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef COCKATRICE_INETWORKSETTINGSPROVIDER_H -#define COCKATRICE_INETWORKSETTINGSPROVIDER_H -#include - -class INetworkSettingsProvider -{ -public: - virtual ~INetworkSettingsProvider() = default; - - virtual QString getClientID() = 0; - - [[nodiscard]] virtual int getTimeOut() const = 0; - [[nodiscard]] virtual int getKeepAlive() const = 0; - [[nodiscard]] virtual bool getNotifyAboutUpdates() const = 0; - - virtual void setKnownMissingFeatures(const QString &_knownMissingFeatures) = 0; - virtual QString getKnownMissingFeatures() = 0; -}; - -#endif // COCKATRICE_INETWORKSETTINGSPROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_database_path_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_database_path_provider.h deleted file mode 100644 index bacd7a2bb..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_database_path_provider.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef COCKATRICE_NOOP_CARD_DATABASE_PATH_PROVIDER_H -#define COCKATRICE_NOOP_CARD_DATABASE_PATH_PROVIDER_H -#include "interface_card_database_path_provider.h" - -class NoopCardDatabasePathProvider : public ICardDatabasePathProvider -{ -public: - [[nodiscard]] QString getCardDatabasePath() const override - { - return ""; - } - [[nodiscard]] QString getCustomCardDatabasePath() const override - { - return ""; - } - [[nodiscard]] QString getTokenDatabasePath() const override - { - return ""; - } - [[nodiscard]] QString getSpoilerCardDatabasePath() const override - { - return ""; - } -}; - -#endif // COCKATRICE_NOOP_CARD_DATABASE_PATH_PROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_preference_provider.h b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_preference_provider.h deleted file mode 100644 index 3781f4b63..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_preference_provider.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef COCKATRICE_NOOP_CARD_PREFERENCE_PROVIDER_H -#define COCKATRICE_NOOP_CARD_PREFERENCE_PROVIDER_H -#include "interface_card_preference_provider.h" - -class NoopCardPreferenceProvider : public ICardPreferenceProvider -{ -public: - [[nodiscard]] QString getCardPreferenceOverride(const QString &) const override - { - return {}; - } - - [[nodiscard]] bool getIncludeRebalancedCards() const override - { - return true; - } -}; - -#endif // COCKATRICE_NOOP_CARD_PREFERENCE_PROVIDER_H diff --git a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h b/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h deleted file mode 100644 index e5027648c..000000000 --- a/libcockatrice_interfaces/libcockatrice/interfaces/noop_card_set_priority_controller.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef COCKATRICE_NOOP_CARD_SET_PRIORITY_CONTROLLER_H -#define COCKATRICE_NOOP_CARD_SET_PRIORITY_CONTROLLER_H - -#include "interface_card_set_priority_controller.h" - -class NoopCardSetPriorityController : public ICardSetPriorityController -{ -public: - void setSortKey(QString /* shortName */, unsigned int /* sortKey */) override - { - } - void setEnabled(QString /* shortName */, bool /* enabled */) override - { - } - void setIsKnown(QString /* shortName */, bool /* isknown */) override - { - } - - unsigned int getSortKey(QString /* shortName */) const override - { - return 0; - } - bool isEnabled(QString /* shortName */) const override - { - return true; - } - bool isKnown(QString /* shortName */) const override - { - return true; - } -}; - -#endif // COCKATRICE_NOOP_CARD_SET_PRIORITY_CONTROLLER_H diff --git a/libcockatrice_models/CMakeLists.txt b/libcockatrice_models/CMakeLists.txt deleted file mode 100644 index 7af35b2d5..000000000 --- a/libcockatrice_models/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -add_subdirectory(libcockatrice/models/database) -add_subdirectory(libcockatrice/models/deck_list) - -add_library(libcockatrice_models INTERFACE) - -target_include_directories(libcockatrice_models INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_models INTERFACE libcockatrice_models_database libcockatrice_models_deck_list) diff --git a/libcockatrice_models/libcockatrice/models/database/CMakeLists.txt b/libcockatrice_models/libcockatrice/models/database/CMakeLists.txt deleted file mode 100644 index 950d6d79f..000000000 --- a/libcockatrice_models/libcockatrice/models/database/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - card_database_model.h - card_database_display_model.h - card/card_completer_proxy_model.h - card/card_search_model.h - card_set/card_sets_model.h - token/token_display_model.h - token/token_edit_model.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_models_database STATIC - ${MOC_SOURCES} - card_database_model.cpp - card_database_display_model.cpp - card/card_completer_proxy_model.cpp - card/card_search_model.cpp - card_set/card_sets_model.cpp - token/token_display_model.cpp - token/token_edit_model.cpp -) - -target_include_directories(libcockatrice_models_database PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_models_database PUBLIC libcockatrice_card libcockatrice_filters ${QT_CORE_MODULE}) diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.cpp b/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.cpp deleted file mode 100644 index 387eb454f..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "card_completer_proxy_model.h" - -CardCompleterProxyModel::CardCompleterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) -{ -} - -bool CardCompleterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - if (filterRegularExpression().pattern().isEmpty()) { - return true; - } - - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - QString data = index.data(Qt::DisplayRole).toString(); - - // Ensure substring matching - return data.contains(filterRegularExpression()); -} diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.h b/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.h deleted file mode 100644 index afb6f1fcf..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card/card_completer_proxy_model.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file card_completer_proxy_model.h - * @ingroup CardDatabaseModels - * @brief TODO: Document this. - */ - -#ifndef CARD_COMPLETER_PROXY_MODEL_H -#define CARD_COMPLETER_PROXY_MODEL_H - -#include - -class CardCompleterProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit CardCompleterProxyModel(QObject *parent = nullptr); - -protected: - [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; -}; - -#endif // CARD_COMPLETER_PROXY_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp deleted file mode 100644 index 6a930c1da..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "card_search_model.h" - -#include "../card_database_display_model.h" -#include "../card_database_model.h" - -#include -#include - -CardSearchModel::CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent) - : QAbstractListModel(parent), sourceModel(sourceModel) -{ -} - -int CardSearchModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - return searchResults.size(); -} - -QVariant CardSearchModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= searchResults.size()) - return QVariant(); - - if (role == Qt::DisplayRole) { - return searchResults.at(index.row()).card->getName(); - } - - return QVariant(); -} - -void CardSearchModel::updateSearchResults(const QString &query) -{ - beginResetModel(); - searchResults.clear(); - - if (query.isEmpty() || !sourceModel) - return; - - // Set the filter for the display model - sourceModel->setCardName(query); - - // Collect matching cards and compute Levenshtein distance - for (int i = 0; i < sourceModel->rowCount(); ++i) { - QModelIndex modelIndex = sourceModel->index(i, 0); - QModelIndex sourceIndex = sourceModel->mapToSource(modelIndex); - CardDatabaseModel *sourceDbModel = qobject_cast(sourceModel->sourceModel()); - - if (!sourceDbModel || !sourceIndex.isValid()) - return; - - CardInfoPtr card = sourceDbModel->getCard(sourceIndex.row()); - - if (!card) - continue; - - int distance = levenshteinDistance(query.toLower(), card->getName().toLower()); - searchResults.append({card, distance}); - } - - // Sort by Levenshtein distance (lower distance = better match) - std::sort(searchResults.begin(), searchResults.end(), - [](const SearchResult &a, const SearchResult &b) { return a.distance < b.distance; }); - - // Keep only the top 5 results - if (searchResults.size() > 10) - searchResults = searchResults.mid(0, 10); - - emit dataChanged(index(0, 0), index(rowCount() - 1, 0)); - emit layoutChanged(); - - endResetModel(); -} diff --git a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h b/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h deleted file mode 100644 index 18be2c55a..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card/card_search_model.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file card_search_model.h - * @ingroup CardDatabaseModels - * @brief TODO: Document this. - */ - -#ifndef CARD_SEARCH_MODEL_H -#define CARD_SEARCH_MODEL_H - -#include "../card_database_display_model.h" - -#include - -class CardSearchModel : public QAbstractListModel -{ - Q_OBJECT -public: - explicit CardSearchModel(CardDatabaseDisplayModel *sourceModel, QObject *parent = nullptr); - - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - void updateSearchResults(const QString &query); // Update results based on input - -private: - struct SearchResult - { - CardInfoPtr card; - int distance; - }; - - CardDatabaseDisplayModel *sourceModel; - QList searchResults; -}; - -#endif // CARD_SEARCH_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp deleted file mode 100644 index 5ce63a939..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "card_database_display_model.h" - -#include "card_database_model.h" - -CardDatabaseDisplayModel::CardDatabaseDisplayModel(QObject *parent) - : QSortFilterProxyModel(parent), isToken(ShowAll), filterString(nullptr) -{ - filterTree = nullptr; - setFilterCaseSensitivity(Qt::CaseInsensitive); - setSortCaseSensitivity(Qt::CaseInsensitive); - - dirtyTimer.setSingleShot(true); - connect(&dirtyTimer, &QTimer::timeout, this, &CardDatabaseDisplayModel::invalidate); - - loadedRowCount = 0; -} - -QMap CardDatabaseDisplayModel::characterTranslation = {{L'“', L'\"'}, - {L'”', L'\"'}, - {L'‘', L'\''}, - {L'’', L'\''}}; - -bool CardDatabaseDisplayModel::canFetchMore(const QModelIndex &index) const -{ - return loadedRowCount < sourceModel()->rowCount(index); -} - -void CardDatabaseDisplayModel::fetchMore(const QModelIndex &index) -{ - int remainder = sourceModel()->rowCount(index) - loadedRowCount; - int itemsToFetch = qMin(100, remainder); - - if (itemsToFetch == 0) { - return; - } - - const auto startIndex = qMin(rowCount(QModelIndex()), loadedRowCount); - beginInsertRows(QModelIndex(), startIndex, startIndex + itemsToFetch - 1); - - loadedRowCount += itemsToFetch; - endInsertRows(); -} - -int CardDatabaseDisplayModel::rowCount(const QModelIndex &parent) const -{ - return QSortFilterProxyModel::rowCount(parent); -} - -bool CardDatabaseDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - - QString leftString = sourceModel()->data(left, CardDatabaseModel::SortRole).toString(); - QString rightString = sourceModel()->data(right, CardDatabaseModel::SortRole).toString(); - - if (!cardName.isEmpty() && left.column() == CardDatabaseModel::NameColumn) { - bool isLeftType = leftString.startsWith(cardName, Qt::CaseInsensitive); - bool isRightType = rightString.startsWith(cardName, Qt::CaseInsensitive); - - // test for an exact match: isLeftType && leftString.size() == cardName.size() - // or an exclusive start match: isLeftType && !isRightType - if (isLeftType && (!isRightType || leftString.size() == cardName.size())) - return true; - - // same checks for the right string - if (isRightType && (!isLeftType || rightString.size() == cardName.size())) - return false; - } else if (right.column() == CardDatabaseModel::PTColumn && left.column() == CardDatabaseModel::PTColumn) { - QStringList leftList = leftString.split("/"); - QStringList rightList = rightString.split("/"); - - if (leftList.size() == 2 && rightList.size() == 2) { - - // cool, have both P/T in list now - int lessThanNum = lessThanNumerically(leftList.at(0), rightList.at(0)); - if (lessThanNum != 0) { - return lessThanNum < 0; - } else { - // power equal, check toughness - return lessThanNumerically(leftList.at(1), rightList.at(1)) < 0; - } - } - } - return QString::localeAwareCompare(leftString, rightString) < 0; -} - -int CardDatabaseDisplayModel::lessThanNumerically(const QString &left, const QString &right) -{ - if (left == right) { - return 0; - } - - bool okLeft, okRight; - float leftNum = left.toFloat(&okLeft); - float rightNum = right.toFloat(&okRight); - - if (okLeft && okRight) { - if (leftNum < rightNum) { - return -1; - } else if (leftNum > rightNum) { - return 1; - } else { - return 0; - } - } - // try and parsing again, for weird ones like "1+*" - QString leftAfterNum = ""; - QString rightAfterNum = ""; - if (!okLeft) { - int leftNumIndex = 0; - for (; leftNumIndex < left.length(); leftNumIndex++) { - if (!left.at(leftNumIndex).isDigit()) { - break; - } - } - if (leftNumIndex != 0) { - leftNum = left.left(leftNumIndex).toFloat(&okLeft); - leftAfterNum = left.right(leftNumIndex); - } - } - if (!okRight) { - int rightNumIndex = 0; - for (; rightNumIndex < right.length(); rightNumIndex++) { - if (!right.at(rightNumIndex).isDigit()) { - break; - } - } - if (rightNumIndex != 0) { - rightNum = right.left(rightNumIndex).toFloat(&okRight); - rightAfterNum = right.right(rightNumIndex); - } - } - if (okLeft && okRight) { - - if (leftNum != rightNum) { - // both parsed as numbers, but different number - if (leftNum < rightNum) { - return -1; - } else { - return 1; - } - } else { - // both parsed, same number, but at least one has something else - // so compare the part after the number - prefer nothing - return QString::localeAwareCompare(leftAfterNum, rightAfterNum); - } - } else if (okLeft) { - return -1; - } else if (okRight) { - return 1; - } - // couldn't parse it, just return String comparison - return QString::localeAwareCompare(left, right); -} -bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const -{ - CardInfoPtr info = static_cast(sourceModel())->getCard(sourceRow); - - if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken())) - return false; - - if (filterString != nullptr) { - if (filterTree != nullptr && !filterTree->acceptsCard(info)) { - return false; - } - return filterString->check(info); - } - - return rowMatchesCardName(info); -} - -bool CardDatabaseDisplayModel::rowMatchesCardName(CardInfoPtr info) const -{ - if (!cardName.isEmpty() && !info->getName().contains(cardName, Qt::CaseInsensitive)) - return false; - - if (!cardNameSet.isEmpty() && !cardNameSet.contains(info->getName())) - return false; - - if (filterTree != nullptr) - return filterTree->acceptsCard(info); - - return true; -} - -void CardDatabaseDisplayModel::clearFilterAll() -{ -#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)) - beginFilterChange(); -#endif - cardName.clear(); - cardText.clear(); - cardTypes.clear(); - cardColors.clear(); - if (filterTree != nullptr) - filterTree->clear(); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) - endFilterChange(QSortFilterProxyModel::Direction::Rows); -#else - invalidateFilter(); -#endif -} - -void CardDatabaseDisplayModel::setFilterTree(FilterTree *_filterTree) -{ - if (this->filterTree != nullptr) - disconnect(this->filterTree, nullptr, this, nullptr); - - this->filterTree = _filterTree; - connect(this->filterTree, &FilterTree::changed, this, &CardDatabaseDisplayModel::filterTreeChanged); - invalidate(); -} - -void CardDatabaseDisplayModel::filterTreeChanged() -{ - invalidate(); -} - -const QString CardDatabaseDisplayModel::sanitizeCardName(const QString &dirtyName, const QMap &table) -{ - std::wstring toReturn = dirtyName.toStdWString(); - for (wchar_t &ch : toReturn) { - if (table.contains(ch)) { - ch = table.value(ch); - } - } - return QString::fromStdWString(toReturn); -} diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h b/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h deleted file mode 100644 index 0c5994a3a..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_database_display_model.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @file card_database_display_model.h - * @ingroup CardDatabaseModels - * @brief The CardDatabaseDisplayModel is a QSortFilterProxyModel that allows applying filters and sorting to a - * CardDatabaseModel. - */ - -#ifndef COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H -#define COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H - -#include -#include -#include - -class FilterTree; -class CardDatabaseDisplayModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - enum FilterBool - { - ShowTrue, - ShowFalse, - ShowAll - }; - -private: - FilterBool isToken; - QString cardName, cardText; - QSet cardNameSet, cardTypes, cardColors; - FilterTree *filterTree; - FilterString *filterString; - int loadedRowCount; - QTimer dirtyTimer; - - /** The translation table that will be used for sanitizeCardName. */ - static QMap characterTranslation; - -public: - explicit CardDatabaseDisplayModel(QObject *parent = nullptr); - void setFilterTree(FilterTree *_filterTree); - void setIsToken(FilterBool _isToken) - { - isToken = _isToken; - emit modelDirty(); - dirty(); - } - - void setCardName(const QString &_cardName) - { - if (filterString != nullptr) { - delete filterString; - filterString = nullptr; - } - cardName = sanitizeCardName(_cardName, characterTranslation); - emit modelDirty(); - dirty(); - } - void setStringFilter(const QString &_src) - { - delete filterString; - filterString = new FilterString(_src); - emit modelDirty(); - dirty(); - } - void setCardNameSet(const QSet &_cardNameSet) - { - cardNameSet = _cardNameSet; - emit modelDirty(); - dirty(); - } - - void dirty() - { - dirtyTimer.start(20); - } - void clearFilterAll(); - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - [[nodiscard]] bool canFetchMore(const QModelIndex &parent) const override; - void fetchMore(const QModelIndex &parent) override; -signals: - void modelDirty(); - -protected: - [[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - static int lessThanNumerically(const QString &left, const QString &right); - [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - [[nodiscard]] bool rowMatchesCardName(CardInfoPtr info) const; - -private slots: - void filterTreeChanged(); - /** Will translate all undesirable characters in DIRTYNAME according to the TABLE. */ - const QString sanitizeCardName(const QString &dirtyName, const QMap &table); -}; - -#endif // COCKATRICE_CARD_DATABASE_DISPLAY_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp deleted file mode 100644 index e33156329..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_database_model.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "card_database_model.h" - -#include -#include - -#define CARDDBMODEL_COLUMNS 6 - -CardDatabaseModel::CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent) - : QAbstractListModel(parent), db(_db), showOnlyCardsFromEnabledSets(_showOnlyCardsFromEnabledSets) -{ - connect(db, &CardDatabase::cardAdded, this, &CardDatabaseModel::cardAdded); - connect(db, &CardDatabase::cardRemoved, this, &CardDatabaseModel::cardRemoved); - connect(db, &CardDatabase::cardDatabaseEnabledSetsChanged, this, - &CardDatabaseModel::cardDatabaseEnabledSetsChanged); - - cardDatabaseEnabledSetsChanged(); -} - -CardDatabaseModel::~CardDatabaseModel() = default; - -int CardDatabaseModel::rowCount(const QModelIndex & /*parent*/) const -{ - return cardList.size(); -} - -int CardDatabaseModel::columnCount(const QModelIndex & /*parent*/) const -{ - return CARDDBMODEL_COLUMNS; -} - -QVariant CardDatabaseModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() >= cardList.size() || index.column() >= CARDDBMODEL_COLUMNS || - (role != Qt::DisplayRole && role != SortRole)) - return QVariant(); - - CardInfoPtr card = cardList.at(index.row()); - switch (index.column()) { - case NameColumn: - return card->getName(); - case SetListColumn: - return card->getSetsNames(); - case ManaCostColumn: - return role == SortRole ? QString("%1%2").arg(card->getCmc(), 4, QChar('0')).arg(card->getManaCost()) - : card->getManaCost(); - case CardTypeColumn: - return card->getCardType(); - case PTColumn: - return card->getPowTough(); - case ColorColumn: - return card->getColors(); - default: - return QVariant(); - } -} - -QVariant CardDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - if (orientation != Qt::Horizontal) - return QVariant(); - switch (section) { - case NameColumn: - return QString(tr("Name")); - case SetListColumn: - return QString(tr("Sets")); - case ManaCostColumn: - return QString(tr("Mana cost")); - case CardTypeColumn: - return QString(tr("Card type")); - case PTColumn: - return QString(tr("P/T")); - case ColorColumn: - return QString(tr("Color(s)")); - default: - return QVariant(); - } -} - -void CardDatabaseModel::cardInfoChanged(CardInfoPtr card) -{ - const int row = cardList.indexOf(card); - if (row == -1) - return; - - emit dataChanged(index(row, 0), index(row, CARDDBMODEL_COLUMNS - 1)); -} - -bool CardDatabaseModel::checkCardHasAtLeastOneEnabledSet(CardInfoPtr card) -{ - if (!showOnlyCardsFromEnabledSets) - return true; - - for (const auto &printings : card->getSets()) { - for (const auto &printing : printings) { - if (printing.getSet()->getEnabled()) - return true; - } - } - - return false; -} - -void CardDatabaseModel::cardDatabaseEnabledSetsChanged() -{ - // remove all the cards no more present in at least one enabled set - for (const CardInfoPtr &card : cardList) { - if (!checkCardHasAtLeastOneEnabledSet(card)) { - cardRemoved(card); - } - } - - // re-check all the card currently not shown, maybe their part of a newly-enabled set - for (const CardInfoPtr &card : db->getCardList()) { - if (!cardListSet.contains(card)) { - cardAdded(card); - } - } -} - -void CardDatabaseModel::cardAdded(CardInfoPtr card) -{ - if (checkCardHasAtLeastOneEnabledSet(card)) { - // add the card if it's present in at least one enabled set - beginInsertRows(QModelIndex(), cardList.size(), cardList.size()); - cardList.append(card); - cardListSet.insert(card); - connect(card.data(), &CardInfo::cardInfoChanged, this, &CardDatabaseModel::cardInfoChanged); - endInsertRows(); - } -} - -void CardDatabaseModel::cardRemoved(CardInfoPtr card) -{ - const int row = cardList.indexOf(card); - if (row == -1) { - return; - } - - beginRemoveRows(QModelIndex(), row, row); - disconnect(card.data(), nullptr, this, nullptr); - cardListSet.remove(card); - card.clear(); - cardList.removeAt(row); - endRemoveRows(); -} diff --git a/libcockatrice_models/libcockatrice/models/database/card_database_model.h b/libcockatrice_models/libcockatrice/models/database/card_database_model.h deleted file mode 100644 index 218cfff92..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_database_model.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file card_database_model.h - * @ingroup CardDatabaseModels - * @brief The CardDatabaseModel maps the cardList contained in the CardDatabase as a QAbstractListModel. - */ - -#ifndef CARDDATABASEMODEL_H -#define CARDDATABASEMODEL_H - -#include -#include -#include -#include - -class CardDatabaseModel : public QAbstractListModel -{ - Q_OBJECT -public: - enum Columns - { - NameColumn, - SetListColumn, - ManaCostColumn, - PTColumn, - CardTypeColumn, - ColorColumn - }; - enum Role - { - SortRole = Qt::UserRole - }; - CardDatabaseModel(CardDatabase *_db, bool _showOnlyCardsFromEnabledSets, QObject *parent = nullptr); - ~CardDatabaseModel() override; - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override; - [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; - [[nodiscard]] QVariant - headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - [[nodiscard]] CardDatabase *getDatabase() const - { - return db; - } - [[nodiscard]] CardInfoPtr getCard(int index) const - { - return cardList[index]; - } - -private: - QList cardList; - QSet cardListSet; // Supports faster lookups in cardDatabaseEnabledSetsChanged() - CardDatabase *db; - bool showOnlyCardsFromEnabledSets; - - inline bool checkCardHasAtLeastOneEnabledSet(CardInfoPtr card); -private slots: - void cardAdded(CardInfoPtr card); - void cardRemoved(CardInfoPtr card); - void cardInfoChanged(CardInfoPtr card); - void cardDatabaseEnabledSetsChanged(); -}; - -#endif diff --git a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp deleted file mode 100644 index f6dc4f9cf..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "card_sets_model.h" - -#include - -SetsModel::SetsModel(CardDatabase *_db, QObject *parent) : QAbstractTableModel(parent), sets(_db->getSetList()) -{ - sets.sortByKey(); - for (const CardSetPtr &set : sets) { - if (set->getEnabled()) - enabledSets.insert(set); - } -} - -SetsModel::~SetsModel() = default; - -int SetsModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - else - return sets.size(); -} - -QVariant SetsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || (index.column() >= NUM_COLS) || (index.row() >= rowCount())) - return QVariant(); - - CardSetPtr set = sets[index.row()]; - - if (index.column() == EnabledCol) { - switch (role) { - case SortRole: - return enabledSets.contains(set) ? "1" : "0"; - case Qt::CheckStateRole: - return static_cast(enabledSets.contains(set) ? Qt::Checked : Qt::Unchecked); - case Qt::DisplayRole: - default: - return QVariant(); - } - } - - if (role != Qt::DisplayRole && role != SortRole) - return QVariant(); - - switch (index.column()) { - case SortKeyCol: - return QString("%1").arg(index.row(), 8, 10, QChar('0')); - case IsKnownCol: - return set->getIsKnown(); - case SetTypeCol: - return set->getSetType(); - case ShortNameCol: - return set->getShortName(); - case LongNameCol: - return set->getLongName(); - case ReleaseDateCol: - return set->getReleaseDate().toString(Qt::ISODate); - default: - return QVariant(); - } -} - -bool SetsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (role == Qt::CheckStateRole && index.column() == EnabledCol) { - toggleRow(index.row(), value == Qt::Checked); - return true; - } - return false; -} - -QVariant SetsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) - return QVariant(); - switch (section) { - case SortKeyCol: - return QString("Key"); /* no tr() for translations needed, column just used for sorting --> hidden */ - case IsKnownCol: - return QString( - "Is known"); /* no tr() for translations needed, column is just used for sorting --> hidden */ - case EnabledCol: - return tr("Enabled"); - case SetTypeCol: - return tr("Set type"); - case ShortNameCol: - return tr("Set code"); - case LongNameCol: - return tr("Long name"); - case ReleaseDateCol: - return tr("Release date"); - default: - return QVariant(); - } -} - -Qt::ItemFlags SetsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - - Qt::ItemFlags flags = QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; - - if (index.column() == EnabledCol) - flags |= Qt::ItemIsUserCheckable; - - return flags; -} - -Qt::DropActions SetsModel::supportedDropActions() const -{ - return Qt::MoveAction; -} - -QMimeData *SetsModel::mimeData(const QModelIndexList &indexes) const -{ - if (indexes.isEmpty()) - return 0; - - SetsMimeData *result = new SetsMimeData(indexes[0].row()); - return qobject_cast(result); -} - -bool SetsModel::dropMimeData(const QMimeData *data, - Qt::DropAction action, - int row, - int /*column*/, - const QModelIndex &parent) -{ - if (action != Qt::MoveAction) - return false; - if (row == -1) { - if (!parent.isValid()) - return false; - row = parent.row(); - } - int oldRow = qobject_cast(data)->getOldRow(); - if (oldRow < row) - row--; - - swapRows(oldRow, row); - - return true; -} - -void SetsModel::toggleRow(int row, bool enable) -{ - CardSetPtr temp = sets.at(row); - - if (enable) - enabledSets.insert(temp); - else - enabledSets.remove(temp); - - emit dataChanged(index(row, EnabledCol), index(row, EnabledCol)); -} - -void SetsModel::toggleRow(int row) -{ - CardSetPtr tmp = sets.at(row); - - if (tmp == nullptr) - return; - - if (enabledSets.contains(tmp)) - enabledSets.remove(tmp); - else - enabledSets.insert(tmp); - - emit dataChanged(index(row, EnabledCol), index(row, EnabledCol)); -} - -void SetsModel::toggleAll(bool enabled) -{ - enabledSets.clear(); - - if (enabled) - for (CardSetPtr set : sets) - enabledSets.insert(set); - - emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); -} - -void SetsModel::swapRows(int oldRow, int newRow) -{ - beginRemoveRows(QModelIndex(), oldRow, oldRow); - CardSetPtr temp = sets.takeAt(oldRow); - endRemoveRows(); - - beginInsertRows(QModelIndex(), newRow, newRow); - sets.insert(newRow, temp); - endInsertRows(); - - emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); -} - -void SetsModel::restoreOriginalOrder() -{ - int numRows = rowCount(); - sets.defaultSort(); - emit dataChanged(index(0, 0), index(numRows - 1, columnCount() - 1)); -} - -void SetsModel::sort(int column, Qt::SortOrder order) -{ - QMultiMap setMap; - int numRows = rowCount(); - int row; - - for (row = 0; row < numRows; ++row) - setMap.insert(index(row, column).data(SetsModel::SortRole).toString(), sets.at(row)); - - QList tmp = setMap.values(); - sets.clear(); - if (order == Qt::AscendingOrder) { - for (row = 0; row < tmp.size(); row++) { - sets.append(tmp.at(row)); - } - } else { - for (row = tmp.size() - 1; row >= 0; row--) { - sets.append(tmp.at(row)); - } - } - - emit dataChanged(index(0, 0), index(numRows - 1, columnCount() - 1)); -} - -void SetsModel::save(CardDatabase *db) -{ - // order - for (int i = 0; i < sets.size(); i++) - sets[i]->setSortKey(static_cast(i + 1)); - - // enabled sets - for (const CardSetPtr &set : sets) - set->setEnabled(enabledSets.contains(set)); - - sets.sortByKey(); - - db->notifyEnabledSetsChanged(); -} - -void SetsModel::restore(CardDatabase *db) -{ - // order - sets = db->getSetList(); - sets.sortByKey(); - - // enabled sets - enabledSets.clear(); - for (const CardSetPtr &set : sets) { - if (set->getEnabled()) - enabledSets.insert(set); - } - - emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); -} - -QStringList SetsModel::mimeTypes() const -{ - return QStringList() << "application/x-cockatricecardset"; -} - -SetsDisplayModel::SetsDisplayModel(QObject *parent) : QSortFilterProxyModel(parent) -{ - setFilterCaseSensitivity(Qt::CaseInsensitive); - setSortCaseSensitivity(Qt::CaseInsensitive); -} - -void SetsDisplayModel::fetchMore(const QModelIndex &index) -{ - int itemsToFetch = sourceModel()->rowCount(index); - - beginInsertRows(QModelIndex(), 0, itemsToFetch - 1); - - endInsertRows(); -} - -bool SetsDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - auto typeIndex = sourceModel()->index(sourceRow, SetsModel::SetTypeCol, sourceParent); - auto nameIndex = sourceModel()->index(sourceRow, SetsModel::LongNameCol, sourceParent); - auto shortNameIndex = sourceModel()->index(sourceRow, SetsModel::ShortNameCol, sourceParent); - - const auto filter = filterRegularExpression(); - - return (sourceModel()->data(typeIndex).toString().contains(filter) || - sourceModel()->data(nameIndex).toString().contains(filter) || - sourceModel()->data(shortNameIndex).toString().contains(filter)); -} - -bool SetsDisplayModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - QString leftString = sourceModel()->data(left, SetsModel::SortRole).toString(); - QString rightString = sourceModel()->data(right, SetsModel::SortRole).toString(); - - return QString::localeAwareCompare(leftString, rightString) < 0; -} diff --git a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.h b/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.h deleted file mode 100644 index 0ffc5a847..000000000 --- a/libcockatrice_models/libcockatrice/models/database/card_set/card_sets_model.h +++ /dev/null @@ -1,106 +0,0 @@ -/** - * @file sets_model.h - * @ingroup CardDatabaseModels - * @brief TODO: Document this. - */ - -#ifndef SETSMODEL_H -#define SETSMODEL_H - -#include -#include -#include -#include -#include - -class SetsProxyModel; - -class SetsMimeData : public QMimeData -{ - Q_OBJECT -private: - int oldRow; - -public: - SetsMimeData(int _oldRow) : oldRow(_oldRow) - { - } - [[nodiscard]] int getOldRow() const - { - return oldRow; - } - [[nodiscard]] QStringList formats() const - { - return QStringList() << "application/x-cockatricecardset"; - } -}; - -class SetsModel : public QAbstractTableModel -{ - Q_OBJECT - friend class SetsProxyModel; - -private: - static const int NUM_COLS = 7; - CardSetList sets; - QSet enabledSets; - -public: - enum SetsColumns - { - SortKeyCol, - IsKnownCol, - EnabledCol, - LongNameCol, - ShortNameCol, - SetTypeCol, - ReleaseDateCol, - PriorityCol - }; - enum Role - { - SortRole = Qt::UserRole - }; - - explicit SetsModel(CardDatabase *_db, QObject *parent = nullptr); - ~SetsModel() override; - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override - { - Q_UNUSED(parent); - return NUM_COLS; - } - [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - [[nodiscard]] QVariant - headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override; - [[nodiscard]] Qt::DropActions supportedDropActions() const override; - - [[nodiscard]] QMimeData *mimeData(const QModelIndexList &indexes) const override; - bool - dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; - [[nodiscard]] QStringList mimeTypes() const override; - void swapRows(int oldRow, int newRow); - void toggleRow(int row, bool enable); - void toggleRow(int row); - void toggleAll(bool); - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; - void save(CardDatabase *db); - void restore(CardDatabase *db); - void restoreOriginalOrder(); -}; - -class SetsDisplayModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit SetsDisplayModel(QObject *parent = nullptr); - -protected: - [[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - void fetchMore(const QModelIndex &index) override; -}; - -#endif diff --git a/libcockatrice_models/libcockatrice/models/database/token/token_display_model.cpp b/libcockatrice_models/libcockatrice/models/database/token/token_display_model.cpp deleted file mode 100644 index 4282c6dc8..000000000 --- a/libcockatrice_models/libcockatrice/models/database/token/token_display_model.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "token_display_model.h" - -#include "../card_database_model.h" - -TokenDisplayModel::TokenDisplayModel(QObject *parent) : CardDatabaseDisplayModel(parent) -{ -} - -bool TokenDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const -{ - CardInfoPtr info = static_cast(sourceModel())->getCard(sourceRow); - return info->getIsToken() && rowMatchesCardName(info); -} - -int TokenDisplayModel::rowCount(const QModelIndex &parent) const -{ - // always load all tokens at start - return QSortFilterProxyModel::rowCount(parent); -} \ No newline at end of file diff --git a/libcockatrice_models/libcockatrice/models/database/token/token_display_model.h b/libcockatrice_models/libcockatrice/models/database/token/token_display_model.h deleted file mode 100644 index f6b2fdfbb..000000000 --- a/libcockatrice_models/libcockatrice/models/database/token/token_display_model.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file token_display_model.h - * @ingroup CardDatabaseModels - * @brief TODO: Document this. - */ - -#ifndef COCKATRICE_TOKEN_DISPLAY_MODEL_H -#define COCKATRICE_TOKEN_DISPLAY_MODEL_H - -#include "../card_database_display_model.h" - -class TokenDisplayModel : public CardDatabaseDisplayModel -{ - Q_OBJECT -public: - explicit TokenDisplayModel(QObject *parent = nullptr); - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - -protected: - [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; -}; - -#endif // COCKATRICE_TOKEN_DISPLAY_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.cpp b/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.cpp deleted file mode 100644 index 89a1f71b1..000000000 --- a/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "token_edit_model.h" - -#include "../card_database_display_model.h" -#include "../card_database_model.h" - -#include - -TokenEditModel::TokenEditModel(QObject *parent) : CardDatabaseDisplayModel(parent) -{ -} - -bool TokenEditModel::filterAcceptsRow(int sourceRow, const QModelIndex & /*sourceParent*/) const -{ - CardInfoPtr info = static_cast(sourceModel())->getCard(sourceRow); - return info->getIsToken() && info->getSets().contains(CardSet::TOKENS_SETNAME) && rowMatchesCardName(info); -} - -int TokenEditModel::rowCount(const QModelIndex &parent) const -{ - // always load all tokens at start - return QSortFilterProxyModel::rowCount(parent); -} \ No newline at end of file diff --git a/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.h b/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.h deleted file mode 100644 index 5e5843761..000000000 --- a/libcockatrice_models/libcockatrice/models/database/token/token_edit_model.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @file token_edit_model.h - * @ingroup CardDatabaseModels - * @brief TODO: Document this. - */ - -#ifndef COCKATRICE_TOKEN_EDIT_MODEL_H -#define COCKATRICE_TOKEN_EDIT_MODEL_H - -#include "../card_database_display_model.h" - -class TokenEditModel : public CardDatabaseDisplayModel -{ - Q_OBJECT -public: - explicit TokenEditModel(QObject *parent = nullptr); - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; - -protected: - [[nodiscard]] bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; -}; - -#endif // COCKATRICE_TOKEN_EDIT_MODEL_H diff --git a/libcockatrice_models/libcockatrice/models/deck_list/CMakeLists.txt b/libcockatrice_models/libcockatrice/models/deck_list/CMakeLists.txt deleted file mode 100644 index 851636a35..000000000 --- a/libcockatrice_models/libcockatrice/models/deck_list/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS deck_list_model.h deck_list_sort_filter_proxy_model.h) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_models_deck_list STATIC ${MOC_SOURCES} deck_list_model.cpp deck_list_sort_filter_proxy_model.cpp -) - -target_include_directories(libcockatrice_models_deck_list PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries( - libcockatrice_models_deck_list PUBLIC libcockatrice_card libcockatrice_deck_list ${QT_CORE_MODULE} -) diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp deleted file mode 100644 index e43c612f0..000000000 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.cpp +++ /dev/null @@ -1,776 +0,0 @@ -#include "deck_list_model.h" - -#include - -DeckListModel::DeckListModel(QObject *parent) - : QAbstractItemModel(parent), lastKnownColumn(1), lastKnownOrder(Qt::AscendingOrder) -{ - deckList = QSharedPointer(new DeckList()); - root = new InnerDecklistNode; -} - -DeckListModel::DeckListModel(QObject *parent, const QSharedPointer &deckList) : DeckListModel(parent) -{ - setDeckList(deckList); - - // forward change signals - connect(this, &DeckListModel::cardAddedAt, this, &DeckListModel::cardsChanged); - connect(this, &DeckListModel::cardRemoved, this, &DeckListModel::cardsChanged); - connect(this, &DeckListModel::deckReplaced, this, &DeckListModel::cardsChanged); - - connect(this, &DeckListModel::cardNodeAddedAt, this, &DeckListModel::cardNodesChanged); - connect(this, &DeckListModel::cardNodeRemoved, this, &DeckListModel::cardNodesChanged); - connect(this, &DeckListModel::deckReplaced, this, &DeckListModel::cardNodesChanged); -} - -DeckListModel::~DeckListModel() -{ - delete root; -} - -/** - * @brief Extract the value from the card that is used for the group criteria. - * @param info Pointer to card information. - * @param criteria The group criteria - * @return String representing the value of the criteria. - */ -static QString extractGroupCriteriaValue(const CardInfoPtr &info, DeckListModelGroupCriteria::Type criteria) -{ - if (!info) { - return "unknown"; - } - - switch (criteria) { - case DeckListModelGroupCriteria::MAIN_TYPE: - return info->getMainCardType(); - case DeckListModelGroupCriteria::MANA_COST: - return info->getCmc(); - case DeckListModelGroupCriteria::COLOR: - return info->getColors() == "" ? "Colorless" : info->getColors(); - default: - return "unknown"; - } -} - -void DeckListModel::rebuildTree() -{ - beginResetModel(); - root->clearTree(); - - InnerDecklistNode *listRoot = deckList->getTree()->getRoot(); - - for (int i = 0; i < listRoot->size(); i++) { - auto *currentZone = dynamic_cast(listRoot->at(i)); - auto *node = new InnerDecklistNode(currentZone->getName(), root); - - for (int j = 0; j < currentZone->size(); j++) { - auto *currentCard = dynamic_cast(currentZone->at(j)); - - // TODO: better sanity checking - if (currentCard == nullptr) { - continue; - } - - CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(currentCard->getName()); - QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria); - - auto *groupNode = dynamic_cast(node->findChild(groupCriteria)); - - if (!groupNode) { - groupNode = new InnerDecklistNode(groupCriteria, node); - } - - new DecklistModelCardNode(currentCard, groupNode); - } - } - - endResetModel(); - - refreshCardFormatLegalities(); -} - -int DeckListModel::rowCount(const QModelIndex &parent) const -{ - // debugIndexInfo("rowCount", parent); - auto *node = getNode(parent); - if (node) { - return node->size(); - } else { - return 0; - } -} - -int DeckListModel::columnCount(const QModelIndex & /*parent*/) const -{ - return 5; -} - -QVariant DeckListModel::data(const QModelIndex &index, int role) const -{ - // debugIndexInfo("data", index); - if (!index.isValid()) { - return {}; - } - - if (index.column() >= columnCount()) { - return {}; - } - - auto *node = static_cast(index.internalPointer()); - auto *card = dynamic_cast(node); - - // Group node - if (!card) { - const auto *group = dynamic_cast(node); - - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: { - switch (index.column()) { - case DeckListModelColumns::CARD_AMOUNT: - return group->recursiveCount(true); - case DeckListModelColumns::CARD_NAME: - if (role == Qt::DisplayRole) { - return group->getVisibleName(); - } - return group->getName(); - case DeckListModelColumns::CARD_SET: - return group->getCardSetShortName(); - case DeckListModelColumns::CARD_COLLECTOR_NUMBER: - return group->getCardCollectorNumber(); - case DeckListModelColumns::CARD_PROVIDER_ID: - return group->getCardProviderId(); - default: - return {}; - } - } - case DeckRoles::IsCardRole: - return false; - - case DeckRoles::DepthRole: - return group->depth(); - - // legality does not apply to group nodes - case DeckRoles::IsLegalRole: - return true; - - default: - return {}; - } - } - - // Card node - switch (role) { - case Qt::DisplayRole: - case Qt::EditRole: - switch (index.column()) { - case DeckListModelColumns::CARD_AMOUNT: - return card->getNumber(); - case DeckListModelColumns::CARD_NAME: - return card->getName(); - case DeckListModelColumns::CARD_SET: - return card->getCardSetShortName(); - case DeckListModelColumns::CARD_COLLECTOR_NUMBER: - return card->getCardCollectorNumber(); - case DeckListModelColumns::CARD_PROVIDER_ID: - return card->getCardProviderId(); - default: - return {}; - } - - case DeckRoles::IsCardRole: { - return true; - } - - case DeckRoles::DepthRole: { - return card->depth(); - } - - case DeckRoles::IsLegalRole: { - return card->getFormatLegality(); - } - - default: { - return {}; - } - } -} - -QVariant DeckListModel::headerData(const int section, const Qt::Orientation orientation, const int role) const -{ - if ((role != Qt::DisplayRole) || (orientation != Qt::Horizontal)) { - return {}; - } - - if (section >= columnCount()) { - return {}; - } - - switch (section) { - case DeckListModelColumns::CARD_AMOUNT: - return tr("Count"); - case DeckListModelColumns::CARD_NAME: - return tr("Card"); - case DeckListModelColumns::CARD_SET: - return tr("Set"); - case DeckListModelColumns::CARD_COLLECTOR_NUMBER: - return tr("Number"); - case DeckListModelColumns::CARD_PROVIDER_ID: - return tr("Provider ID"); - default: - return {}; - } -} - -QModelIndex DeckListModel::index(int row, int column, const QModelIndex &parent) const -{ - // debugIndexInfo("index", parent); - if (!hasIndex(row, column, parent)) { - return {}; - } - - auto *parentNode = getNode(parent); - return row >= parentNode->size() ? QModelIndex() : createIndex(row, column, parentNode->at(row)); -} - -QModelIndex DeckListModel::parent(const QModelIndex &ind) const -{ - if (!ind.isValid()) { - return {}; - } - - return nodeToIndex(static_cast(ind.internalPointer())->getParent()); -} - -Qt::ItemFlags DeckListModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) { - return Qt::NoItemFlags; - } - - Qt::ItemFlags result = Qt::ItemIsEnabled; - result |= Qt::ItemIsSelectable; - - return result; -} - -void DeckListModel::emitBackgroundUpdates(const QModelIndex &parent) -{ - int rows = rowCount(parent); - if (rows == 0) - return; - - QModelIndex topLeft = index(0, 0, parent); - QModelIndex bottomRight = index(rows - 1, columnCount() - 1, parent); - emit dataChanged(topLeft, bottomRight, {Qt::BackgroundRole}); - - for (int r = 0; r < rows; ++r) { - QModelIndex child = index(r, 0, parent); - emitBackgroundUpdates(child); - } -} - -void DeckListModel::emitRecursiveUpdates(const QModelIndex &index) -{ - if (!index.isValid()) { - return; - } - - emit dataChanged(index, index); - emitRecursiveUpdates(index.parent()); -} - -bool DeckListModel::setData(const QModelIndex &index, const QVariant &value, const int role) -{ - auto *node = getNode(index); - if (!node || (role != Qt::EditRole)) { - return false; - } - - switch (index.column()) { - case DeckListModelColumns::CARD_AMOUNT: - node->setNumber(value.toInt()); - refreshCardFormatLegalities(); - break; - case DeckListModelColumns::CARD_NAME: - node->setName(value.toString()); - break; - case DeckListModelColumns::CARD_SET: - node->setCardSetShortName(value.toString()); - break; - case DeckListModelColumns::CARD_COLLECTOR_NUMBER: - node->setCardCollectorNumber(value.toString()); - break; - case DeckListModelColumns::CARD_PROVIDER_ID: - node->setCardProviderId(value.toString()); - break; - default: - return false; - } - - emitRecursiveUpdates(index); - deckList->refreshDeckHash(); - emit deckHashChanged(); - - return true; -} - -bool DeckListModel::removeRows(int row, int count, const QModelIndex &parent) -{ - auto *node = getNode(parent); - if (!node) { - return false; - } - - if (row + count > node->size()) { - return false; - } - - beginRemoveRows(parent, row, row + count - 1); - for (int i = 0; i < count; i++) { - AbstractDecklistNode *toDelete = node->takeAt(row); - if (auto *temp = dynamic_cast(toDelete)) { - deckList->getTree()->deleteNode(temp->getDataNode()); - } - delete toDelete; - } - endRemoveRows(); - - if (node->empty() && (node != root)) { - removeRows(parent.row(), 1, parent.parent()); - } else { - emitRecursiveUpdates(parent); - } - - deckList->refreshDeckHash(); - emit deckHashChanged(); - - return true; -} - -InnerDecklistNode *DeckListModel::createNodeIfNeeded(const QString &name, InnerDecklistNode *parent) -{ - auto *newNode = dynamic_cast(parent->findChild(name)); - if (!newNode) { - beginInsertRows(nodeToIndex(parent), parent->size(), parent->size()); - newNode = new InnerDecklistNode(name, parent); - endInsertRows(); - } - return newNode; -} - -DecklistModelCardNode *DeckListModel::findCardNode(const QString &cardName, - const QString &zoneName, - const QString &providerId, - const QString &cardNumber) const -{ - InnerDecklistNode *zoneNode = dynamic_cast(root->findChild(zoneName)); - if (!zoneNode) { - return nullptr; - } - - CardInfoPtr info = CardDatabaseManager::query()->getCardInfo(cardName); - if (!info) { - return nullptr; - } - - QString groupCriteria = extractGroupCriteriaValue(info, activeGroupCriteria); - InnerDecklistNode *groupNode = dynamic_cast(zoneNode->findChild(groupCriteria)); - if (!groupNode) { - return nullptr; - } - - return dynamic_cast( - groupNode->findCardChildByNameProviderIdAndNumber(cardName, providerId, cardNumber)); -} - -QModelIndex DeckListModel::findCard(const QString &cardName, - const QString &zoneName, - const QString &providerId, - const QString &cardNumber) const -{ - DecklistModelCardNode *cardNode = findCardNode(cardName, zoneName, providerId, cardNumber); - if (!cardNode) { - return {}; - } - - return nodeToIndex(cardNode); -} - -QModelIndex DeckListModel::addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway) -{ - ExactCard card = CardDatabaseManager::query()->getCard({cardName}); - - if (!card) { - if (abAddAnyway) { - // We need to keep this card added no matter what - // This is usually called from tab_deck_editor - // So we'll create a new CardInfo with the name - // and default values for all fields - card = ExactCard(CardInfo::newInstance(cardName)); - } else { - return {}; - } - } - - return addCard(card, zoneName); -} - -QModelIndex DeckListModel::addCard(const ExactCard &card, const QString &zoneName) -{ - if (!card) { - return {}; - } - - InnerDecklistNode *zoneNode = createNodeIfNeeded(zoneName, root); - - CardInfoPtr cardInfo = card.getCardPtr(); - PrintingInfo printingInfo = card.getPrinting(); - - QString groupCriteria = extractGroupCriteriaValue(cardInfo, activeGroupCriteria); - InnerDecklistNode *groupNode = createNodeIfNeeded(groupCriteria, zoneNode); - - const QModelIndex parentIndex = nodeToIndex(groupNode); - auto *cardNode = dynamic_cast(groupNode->findCardChildByNameProviderIdAndNumber( - card.getName(), printingInfo.getUuid(), printingInfo.getProperty("num"))); - const auto cardSetName = printingInfo.getSet().isNull() ? "" : printingInfo.getSet()->getCorrectedShortName(); - - bool cardNodeAdded = false; - if (!cardNode) { - // Determine the correct index - int insertRow = findSortedInsertRow(groupNode, cardInfo); - - auto *decklistCard = deckList->addCard(cardInfo->getName(), zoneName, insertRow, cardSetName, - printingInfo.getProperty("num"), printingInfo.getProperty("uuid")); - - beginInsertRows(parentIndex, insertRow, insertRow); - cardNode = new DecklistModelCardNode(decklistCard, groupNode, insertRow); - endInsertRows(); - - cardNodeAdded = true; - } else { - cardNode->setNumber(cardNode->getNumber() + 1); - cardNode->setCardSetShortName(cardSetName); - cardNode->setCardCollectorNumber(printingInfo.getProperty("num")); - cardNode->setCardProviderId(printingInfo.getProperty("uuid")); - // Emit dataChanged for the amount column since we modified it - QModelIndex cardIndex = nodeToIndex(cardNode); - QModelIndex amountIndex = cardIndex.sibling(cardIndex.row(), DeckListModelColumns::CARD_AMOUNT); - emit dataChanged(amountIndex, amountIndex, {Qt::EditRole}); - } - sort(lastKnownColumn, lastKnownOrder); - refreshCardFormatLegalities(); - emitRecursiveUpdates(parentIndex); - - deckList->refreshDeckHash(); - emit deckHashChanged(); - - auto index = nodeToIndex(cardNode); - - if (cardNodeAdded) { - emit cardNodeAddedAt(index); - } - - emit cardAddedAt(index); - - return index; -} - -bool DeckListModel::offsetCountAtIndex(const QModelIndex &idx, int offset) -{ - if (!idx.isValid()) { - return false; - } - - auto *node = static_cast(idx.internalPointer()); - auto *card = dynamic_cast(node); - - if (!card) { - return false; - } - - const QModelIndex numberIndex = idx.siblingAtColumn(DeckListModelColumns::CARD_AMOUNT); - const int count = numberIndex.data(Qt::EditRole).toInt(); - const int newCount = count + offset; - - if (newCount <= 0) { - removeRow(idx.row(), idx.parent()); - emit cardNodeRemoved(); - } else { - setData(numberIndex, newCount, Qt::EditRole); - } - - if (offset > 0) { - emit cardAddedAt(idx); - } else if (offset < 0) { - emit cardRemoved(); - } - - return true; -} - -bool DeckListModel::removeCardAtIndex(const QModelIndex &idx) -{ - if (!idx.isValid()) { - return false; - } - - auto *node = static_cast(idx.internalPointer()); - auto *card = dynamic_cast(node); - - if (!card) { - return false; - } - - bool success = removeRow(idx.row(), idx.parent()); - - if (success) { - emit cardRemoved(); - } - - return success; -} - -int DeckListModel::findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const -{ - if (!cardInfo) { - return parent->size(); // fallback: append at end - } - - for (int i = 0; i < parent->size(); ++i) { - auto *existingCard = dynamic_cast(parent->at(i)); - if (!existingCard) - continue; - - bool lessThan = false; - switch (lastKnownColumn) { - case 0: // ByNumber - lessThan = lastKnownOrder == Qt::AscendingOrder - ? cardInfo->getProperty("collectorNumber") < existingCard->getCardCollectorNumber() - : cardInfo->getProperty("collectorNumber") > existingCard->getCardCollectorNumber(); - break; - case 1: // ByName - default: - lessThan = lastKnownOrder == Qt::AscendingOrder - ? cardInfo->getName().localeAwareCompare(existingCard->getName()) < 0 - : cardInfo->getName().localeAwareCompare(existingCard->getName()) > 0; - break; - } - - if (lessThan) - return i; - } - - return parent->size(); // insert at end if no earlier match -} - -QModelIndex DeckListModel::nodeToIndex(AbstractDecklistNode *node) const -{ - if (node == nullptr || node == root) { - return {}; - } - - return createIndex(node->getParent()->indexOf(node), 0, node); -} - -void DeckListModel::sortHelper(InnerDecklistNode *node, Qt::SortOrder order) -{ - // Sort children of node and save the information needed to - // update the list of persistent indexes. - QVector> sortResult = node->sort(order); - - QModelIndexList from, to; - int columns = columnCount(); - for (int i = sortResult.size() - 1; i >= 0; --i) { - const int fromRow = sortResult[i].first; - const int toRow = sortResult[i].second; - AbstractDecklistNode *temp = node->at(toRow); - for (int j = 0; j < columns; ++j) { - from << createIndex(fromRow, j, temp); - to << createIndex(toRow, j, temp); - } - } - changePersistentIndexList(from, to); - - // Recursion - for (int i = node->size() - 1; i >= 0; --i) { - auto *subNode = dynamic_cast(node->at(i)); - if (subNode) { - sortHelper(subNode, order); - } - } -} - -void DeckListModel::sort(int column, Qt::SortOrder order) -{ - lastKnownColumn = column; - lastKnownOrder = order; - - emit layoutAboutToBeChanged(); - DeckSortMethod sortMethod; - switch (column) { - case 0: - sortMethod = ByNumber; - break; - case 1: - sortMethod = ByName; - break; - default: - sortMethod = ByName; - } - - root->setSortMethod(sortMethod); - sortHelper(root, order); - emit layoutChanged(); -} - -void DeckListModel::setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria) -{ - activeGroupCriteria = newCriteria; - rebuildTree(); -} - -void DeckListModel::setActiveFormat(const QString &_format) -{ - deckList->setGameFormat(_format); - refreshCardFormatLegalities(); - emitBackgroundUpdates(QModelIndex()); // start from root -} - -void DeckListModel::cleanList() -{ - setDeckList(QSharedPointer(new DeckList())); -} - -/** - * @param _deck The deck. - */ -void DeckListModel::setDeckList(const QSharedPointer &_deck) -{ - if (deckList != _deck) { - deckList = _deck; - } - rebuildTree(); - emit deckReplaced(); -} - -void DeckListModel::forEachCard(const std::function &func) -{ - deckList->forEachCard(func); -} - -QList DeckListModel::getCardNodes() const -{ - return deckList->getCardNodes(); -} - -QList DeckListModel::getCardNodesForZone(const QString &zoneName) const -{ - return deckList->getCardNodes({zoneName}); -} - -QList DeckListModel::getCardNames() const -{ - auto nodes = deckList->getCardNodes(); - - QList names; - std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(names), [](auto node) { return node->getName(); }); - - return names; -} - -QList DeckListModel::getCardRefs() const -{ - auto nodes = deckList->getCardNodes(); - - QList cardRefs; - std::transform(nodes.cbegin(), nodes.cend(), std::back_inserter(cardRefs), - [](auto node) { return node->toCardRef(); }); - - return cardRefs; -} - -QList DeckListModel::getZones() const -{ - auto zoneNodes = deckList->getZoneNodes(); - - QList zones; - std::transform(zoneNodes.cbegin(), zoneNodes.cend(), std::back_inserter(zones), - [](auto zoneNode) { return zoneNode->getName(); }); - - return zones; -} - -static int maxAllowedForLegality(const FormatRules &format, const QString &legality) -{ - for (const AllowedCount &c : format.allowedCounts) { - if (c.label == legality) { - return c.max; - } - } - return -1; // unknown legality → treat as illegal -} - -static bool isCardQuantityLegalForFormat(const QString &format, const CardInfo &cardInfo, int quantity) -{ - if (format.isEmpty()) { - return true; - } - - auto formatRules = CardDatabaseManager::query()->getFormat(format); - - // if format has no custom rules, then just do the default check - if (!formatRules) { - return cardInfo.isLegalInFormat(format); - } - - // Exceptions always win - if (cardHasAnyException(cardInfo, *formatRules)) { - return true; - } - - // check legality prop - const QString legality = cardInfo.getLegalityProp(format); - if (legality.isEmpty()) { - return false; - } - - int maxAllowed = maxAllowedForLegality(*formatRules, legality); - - if (maxAllowed == -1) { - return false; - } - - if (maxAllowed < 0) { // unlimited - return true; - } - - return quantity <= maxAllowed; -} - -static bool isCardNodeLegalForFormat(const QString &format, const InnerDecklistNode *zone, const DecklistCardNode *card) -{ - // Don't check legality for tokens - if (zone->getName() == DECK_ZONE_TOKENS) { - return true; - } - - // unknown cards are not legal - ExactCard exactCard = CardDatabaseManager::query()->getCard(card->toCardRef()); - if (!exactCard) { - return false; - } - - // actual check - return isCardQuantityLegalForFormat(format, exactCard.getInfo(), card->getNumber()); -} - -void DeckListModel::refreshCardFormatLegalities() -{ - QString format = deckList->getGameFormat(); - - deckList->forEachCard([&format](const InnerDecklistNode *zone, DecklistCardNode *card) { - bool legal = isCardNodeLegalForFormat(format, zone, card); - card->setFormatLegality(legal); - }); -} diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h deleted file mode 100644 index 86d36b7f9..000000000 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_model.h +++ /dev/null @@ -1,438 +0,0 @@ -#ifndef DECKLISTMODEL_H -#define DECKLISTMODEL_H - -#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/abstract_deck_list_card_node.h> -#include <../../../../libcockatrice_deck_list/libcockatrice/deck_list/tree/deck_list_card_node.h> -#include -#include -#include -#include - -class CardDatabase; -class QPrinter; -class QTextCursor; - -/** - * @namespace DeckRoles - * @brief Custom model roles used by the DeckListModel for data retrieval. - * - * These roles extend Qt's item data roles starting at Qt::UserRole. - */ -namespace DeckRoles -{ -/** - * @enum DeckRoles - * @brief Custom data roles for deck-related model items. - * - * These roles are used to retrieve specialized data from the DeckListModel. - */ -enum -{ - IsCardRole = Qt::UserRole + 1, /**< Indicates whether the item represents a card. */ - DepthRole, /**< Depth level within the deck's grouping hierarchy. */ - IsLegalRole /**< Whether the card is legal in the current deck format. */ -}; -} // namespace DeckRoles - -/** - * @namespace DeckListModelColumns - * @brief Column indices for the DeckListModel. - * - * These values map to the columns in the deck list table representation. - */ -namespace DeckListModelColumns -{ -/** - * @enum DeckListModelColumns - * @brief Column identifiers for displaying card information in the deck list. - */ -enum -{ - CARD_AMOUNT = 0, /**< The number of copies of the card. */ - CARD_NAME = 1, /**< The card's name. */ - CARD_SET = 2, /**< The set or expansion the card belongs to. */ - CARD_COLLECTOR_NUMBER = 3, /**< Collector number of the card within the set. */ - CARD_PROVIDER_ID = 4 /**< ID used by the external data provider (e.g., Scryfall). */ -}; -} // namespace DeckListModelColumns - -/** - * @namespace DeckListModelGroupCriteria - * @brief Specifies criteria used to group cards in the DeckListModel. - * - * These values determine how cards are grouped in UI views such as the deck editor. - */ -namespace DeckListModelGroupCriteria -{ -/** - * @enum DeckListModelGroupCriteria - * @brief Available grouping strategies for deck visualization. - */ -enum Type -{ - MAIN_TYPE, /**< Group cards by their main type (e.g., creature, instant). */ - MANA_COST, /**< Group cards by their total mana cost. */ - COLOR /**< Group cards by their color identity. */ -}; -static inline QString toString(Type t) -{ - switch (t) { - case MAIN_TYPE: - return "Main Type"; - case MANA_COST: - return "Mana Cost"; - case COLOR: - return "Colors"; - } - return {}; -} - -static inline Type fromString(const QString &s) -{ - if (s == "Main Type") - return MAIN_TYPE; - if (s == "Mana Cost") - return MANA_COST; - if (s == "Colors") - return COLOR; - return MAIN_TYPE; // default -} -} // namespace DeckListModelGroupCriteria - -/** - * @class DecklistModelCardNode - * @ingroup DeckModels - * @brief Adapter node that wraps a DecklistCardNode for use in the DeckListModel tree. - * - * This class forwards all property accessors (name, number, provider ID, set info, etc.) - * to the underlying DecklistCardNode. It exists so the model can represent cards - * in the same hierarchy as InnerDecklistNode containers. - */ -class DecklistModelCardNode : public AbstractDecklistCardNode -{ -private: - DecklistCardNode *dataNode; /**< Pointer to the underlying data node. */ - -public: - /** - * @brief Constructs a model node wrapping a DecklistCardNode. - * @param _dataNode The underlying DecklistCardNode to wrap. - * @param _parent The parent InnerDecklistNode in the model tree. - * @param position Optional position to insert in parent (-1 appends at end). - */ - DecklistModelCardNode(DecklistCardNode *_dataNode, InnerDecklistNode *_parent, int position = -1) - : AbstractDecklistCardNode(_parent, position), dataNode(_dataNode) - { - } - [[nodiscard]] int getNumber() const override - { - return dataNode->getNumber(); - } - void setNumber(int _number) override - { - dataNode->setNumber(_number); - } - [[nodiscard]] QString getName() const override - { - return dataNode->getName(); - } - void setName(const QString &_name) override - { - dataNode->setName(_name); - } - [[nodiscard]] QString getCardProviderId() const override - { - return dataNode->getCardProviderId(); - } - void setCardProviderId(const QString &_cardProviderId) override - { - dataNode->setCardProviderId(_cardProviderId); - } - [[nodiscard]] QString getCardSetShortName() const override - { - return dataNode->getCardSetShortName(); - } - void setCardSetShortName(const QString &_cardSetShortName) override - { - dataNode->setCardSetShortName(_cardSetShortName); - } - [[nodiscard]] QString getCardCollectorNumber() const override - { - return dataNode->getCardCollectorNumber(); - } - void setCardCollectorNumber(const QString &_cardSetNumber) override - { - dataNode->setCardCollectorNumber(_cardSetNumber); - } - bool getFormatLegality() const override - { - return dataNode->getFormatLegality(); - } - void setFormatLegality(const bool _formatLegal) override - { - dataNode->setFormatLegality(_formatLegal); - } - - /** - * @brief Returns the underlying data node. - * @return Pointer to the DecklistCardNode wrapped by this node. - */ - [[nodiscard]] DecklistCardNode *getDataNode() const - { - return dataNode; - } - [[nodiscard]] bool isDeckHeader() const override - { - return false; - } -}; - -/** - * @class DeckListModel - * @ingroup DeckModels - * @brief Qt model representing a decklist for use in views (tree/table). - * - * DeckListModel is a QAbstractItemModel that exposes the structure of a deck - * (zones and cards) to Qt views. It organizes cards hierarchically under - * InnerDecklistNode containers and supports grouping, sorting, adding/removing - * cards, and printing decklists. - * - * Outside code should refrain from modifying the model with methods inherited from QAbstractItemModel, such as with - * `setData` or `removeRow`. - * Instead, use the custom methods on this class to modify the model, such as `addCard`, `offsetCountAtIndex`, or - * `removeCardAtIndex`. - * This ensures the custom signals for this class are correctly emitted. - * - * Signals: - * - deckHashChanged(): emitted when the deck contents change in a way that - * affects its hash. - * - * Slots: - * - rebuildTree(): rebuilds the model structure from the underlying node tree. - */ -class DeckListModel : public QAbstractItemModel -{ - Q_OBJECT - -public slots: - /** - * @brief Rebuilds the model tree from the underlying node tree. - * - * This updates all indices and ensures the model reflects the current - * state of the deck. - */ - void rebuildTree(); - - /** - * @brief Sets the criteria used to group cards in the model. - * @param newCriteria The new grouping criteria. - */ - void setActiveGroupCriteria(DeckListModelGroupCriteria::Type newCriteria); - - void setActiveFormat(const QString &_format); - -signals: - /** - * @brief Emitted whenever the deck hash changes due to modifications in the model. - */ - void deckHashChanged(); - - /** - * @brief Emitted whenever the cards in the deck changes. This includes when the deck is replaced. - */ - void cardsChanged(); - - /** - * @brief Emitted whenever a card is added to the deck, regardless of whether it's an entirely new card or an - * existing card that got incremented. - * @param index The index of the card that got added. - */ - void cardAddedAt(const QModelIndex &index); - - /** - * @brief Emitted whenever a card is removed from the deck, regardless of whether a card node was removed or an - * existing card got decremented. - */ - void cardRemoved(); - - /** - * @brief Emitted whenever a card node is added or removed. This includes when the deck is replaced. - */ - void cardNodesChanged(); - - /** - * @brief Emitted whenever a new card node is added. - * @param index The index of the card node that got added. - */ - void cardNodeAddedAt(const QModelIndex &index); - - /** - * @brief Emitted whenever a card node is removed. - */ - void cardNodeRemoved(); - - /** - * @brief Emitted whenever the deck in the model has been replaced with a new one - */ - void deckReplaced(); - -public: - explicit DeckListModel(QObject *parent = nullptr); - explicit DeckListModel(QObject *parent, const QSharedPointer &deckList); - ~DeckListModel() override; - - /** - * @brief Returns the root index of the model. - * @return QModelIndex representing the root node. - */ - [[nodiscard]] QModelIndex getRoot() const - { - return nodeToIndex(root); - } - - /// @name Qt model overrides - ///@{ - [[nodiscard]] int rowCount(const QModelIndex &parent) const override; - [[nodiscard]] int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override; - [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; - [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - [[nodiscard]] QModelIndex index(int row, int column, const QModelIndex &parent) const override; - [[nodiscard]] QModelIndex parent(const QModelIndex &index) const override; - [[nodiscard]] Qt::ItemFlags flags(const QModelIndex &index) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - bool removeRows(int row, int count, const QModelIndex &parent) override; - void sort(int column, Qt::SortOrder order) override; - ///@} - - /** - * @brief Finds a card by name, zone, and optional identifiers. - * @param cardName The card's name. - * @param zoneName The zone to search in (main/side/etc.). - * @param providerId Optional provider-specific ID. - * @param cardNumber Optional collector number. - * @return QModelIndex of the card, or invalid index if not found. - */ - [[nodiscard]] QModelIndex findCard(const QString &cardName, - const QString &zoneName, - const QString &providerId = "", - const QString &cardNumber = "") const; - - /** - * @brief Adds a card using the preferred printing if available. - * - * @param cardName Name of the card to add. - * @param zoneName Zone to insert the card into. - * @param abAddAnyway Whether to add the card even if resolution fails. - * @return QModelIndex pointing to the newly inserted card node. - */ - QModelIndex addPreferredPrintingCard(const QString &cardName, const QString &zoneName, bool abAddAnyway); - - /** - * @brief Adds an ExactCard to the specified zone. - * @param card The card to add. - * @param zoneName The zone to insert the card into. - * @return QModelIndex pointing to the newly inserted card node. - */ - QModelIndex addCard(const ExactCard &card, const QString &zoneName); - - /** - * @brief Changes the `amount` field in the card node at the index by the amount. - * Removes the node if it causes the amount to fall to 0 or below. - * @param idx The index of a card node. No-ops if the index is invalid or not a card node. - * @param offset The amount to change the amount field by. - * @return Whether the operation was successful - */ - bool offsetCountAtIndex(const QModelIndex &idx, int offset); - - /** - * @brief Removes the card node at the index - * @param idx The index of a card node. No-ops if the index is invalid or not a card node. - * @return Whether the node was removed. - */ - bool removeCardAtIndex(const QModelIndex &idx); - - /** - * @brief Removes all cards and resets the model. - */ - void cleanList(); - - [[nodiscard]] QSharedPointer getDeckList() const - { - return deckList; - } - void setDeckList(const QSharedPointer &_deck); - - /** - * @brief Apply a function to every card in the deck tree. - * - * @param func Function taking (zone node, card node). - */ - void forEachCard(const std::function &func); - - /** - * @brief Gets a list of all card nodes in the deck. - */ - [[nodiscard]] QList getCardNodes() const; - [[nodiscard]] QList getCardNodesForZone(const QString &zoneName) const; - - /** - * @brief Gets a deduplicated list of all card names that appear in the model - */ - [[nodiscard]] QList getCardNames() const; - /** - * @brief Gets a deduplicated list of all CardRefs that appear in the model - */ - [[nodiscard]] QList getCardRefs() const; - /** - * @brief Gets a list of all zone names that appear in the model - */ - [[nodiscard]] QList getZones() const; - -private: - QSharedPointer deckList; /**< Pointer to the decklist providing the underlying data. */ - InnerDecklistNode *root; /**< Root node of the model tree. */ - DeckListModelGroupCriteria::Type activeGroupCriteria = DeckListModelGroupCriteria::MAIN_TYPE; - int lastKnownColumn; /**< Last column used for sorting. */ - Qt::SortOrder lastKnownOrder; /**< Last known sort order. */ - - InnerDecklistNode *createNodeIfNeeded(const QString &name, InnerDecklistNode *parent); - QModelIndex nodeToIndex(AbstractDecklistNode *node) const; - [[nodiscard]] DecklistModelCardNode *findCardNode(const QString &cardName, - const QString &zoneName, - const QString &providerId = "", - const QString &cardNumber = "") const; - - /** - * @brief Determines the sorted insertion row for a card. - * @param parent The parent node where the card will be inserted. - * @param cardInfo The card info to insert. - * @return Row index where the card should be inserted to maintain sort order. - */ - int findSortedInsertRow(const InnerDecklistNode *parent, const CardInfoPtr &cardInfo) const; - - /** - * @brief Recursively emits the dataChanged signal with role as Qt::BackgroundRole for all indices that are children - * of the given node. This is used to update the background color when changing formats. - * @param parent The parent node - */ - void emitBackgroundUpdates(const QModelIndex &parent); - - /** - * @brief Recursively emits the dataChanged signal for the given node and all parent nodes. - * @param index The parent node - */ - void emitRecursiveUpdates(const QModelIndex &index); - - void sortHelper(InnerDecklistNode *node, Qt::SortOrder order); - - template T getNode(const QModelIndex &index) const - { - if (!index.isValid()) - return dynamic_cast(root); - return dynamic_cast(static_cast(index.internalPointer())); - } - - void refreshCardFormatLegalities(); -}; - -#endif diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp deleted file mode 100644 index 0ec159737..000000000 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "deck_list_sort_filter_proxy_model.h" - -#include "deck_list_model.h" - -bool DeckListSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - auto *src = sourceModel(); - - // Inner nodes? -> sort alphabetically by column 1 - bool leftIsCard = src->data(left, Qt::UserRole + 1).toBool(); - bool rightIsCard = src->data(right, Qt::UserRole + 1).toBool(); - - if (!leftIsCard || !rightIsCard) { - QString lName = src->data(left.siblingAtColumn(DeckListModelColumns::CARD_NAME), Qt::EditRole).toString(); - QString rName = src->data(right.siblingAtColumn(DeckListModelColumns::CARD_NAME), Qt::EditRole).toString(); - return lName.localeAwareCompare(rName) < 0; - } - - // Both are cards -> apply sort criteria - auto *lNode = static_cast(left.internalPointer()); - auto *rNode = static_cast(right.internalPointer()); - - CardInfoPtr lInfo = CardDatabaseManager::query()->guessCard({lNode->getName()}).getCardPtr(); - CardInfoPtr rInfo = CardDatabaseManager::query()->guessCard({rNode->getName()}).getCardPtr(); - - // Example: multiple tie-break criteria (colors > cmc > name) - for (const QString &crit : sortCriteria) { - if (crit == "name") { - QString ln = lNode->getName(); - QString rn = rNode->getName(); - int cmp = ln.localeAwareCompare(rn); - if (cmp != 0) - return cmp < 0; - } else if (crit == "cmc") { - int lc = lInfo ? lInfo->getCmc().toInt() : 0; - int rc = rInfo ? rInfo->getCmc().toInt() : 0; - if (lc != rc) - return lc < rc; - } else if (crit == "colors") { - QString lr = lInfo ? lInfo->getColors() : QString(); - QString rr = rInfo ? rInfo->getColors() : QString(); - int cmp = lr.localeAwareCompare(rr); - if (cmp != 0) - return cmp < 0; - } else if (crit == "maintype") { - QString lr = lInfo ? lInfo->getMainCardType() : QString(); - QString rr = rInfo ? rInfo->getMainCardType() : QString(); - int cmp = lr.localeAwareCompare(rr); - if (cmp != 0) - return cmp < 0; - } - } - - return false; -} diff --git a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.h b/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.h deleted file mode 100644 index 94742795d..000000000 --- a/libcockatrice_models/libcockatrice/models/deck_list/deck_list_sort_filter_proxy_model.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file deck_list_sort_filter_proxy_model.h - * @ingroup DeckEditorCardGroupWidgets - * @brief TODO: Document this. - */ - -#ifndef COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H -#define COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H - -#include -#include - -class DeckListSortFilterProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit DeckListSortFilterProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) - { - } - - void setSortCriteria(const QStringList &criteria) - { - sortCriteria = criteria; - invalidate(); // re-sort - } - -protected: - [[nodiscard]] bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - -private: - QStringList sortCriteria; -}; - -#endif // COCKATRICE_DECK_LIST_SORT_FILTER_PROXY_MODEL_H diff --git a/libcockatrice_network/CMakeLists.txt b/libcockatrice_network/CMakeLists.txt deleted file mode 100644 index 3069c0db4..000000000 --- a/libcockatrice_network/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -add_subdirectory(libcockatrice/network/client) -add_subdirectory(libcockatrice/network/server) - -add_library(libcockatrice_network INTERFACE) - -target_include_directories(libcockatrice_network INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries( - libcockatrice_network INTERFACE ${COCKATRICE_QT_MODULES} libcockatrice_network_client libcockatrice_network_server -) diff --git a/libcockatrice_network/libcockatrice/network/client/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/client/CMakeLists.txt deleted file mode 100644 index bcf62463c..000000000 --- a/libcockatrice_network/libcockatrice/network/client/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -add_subdirectory(abstract) -add_subdirectory(local) -add_subdirectory(remote) - -add_library(libcockatrice_network_client INTERFACE) - -target_include_directories(libcockatrice_network_client INTERFACE .) - -target_link_libraries( - libcockatrice_network_client - INTERFACE ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::WebSockets - libcockatrice_network_client_abstract libcockatrice_network_client_local - libcockatrice_network_client_remote -) diff --git a/libcockatrice_network/libcockatrice/network/client/abstract/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/client/abstract/CMakeLists.txt deleted file mode 100644 index 2753246de..000000000 --- a/libcockatrice_network/libcockatrice/network/client/abstract/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS abstract_client.h) - -set(SOURCES abstract_client.cpp) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library(libcockatrice_network_client_abstract STATIC ${MOC_SOURCES} ${SOURCES}) - -add_dependencies(libcockatrice_network_client_abstract libcockatrice_protocol libcockatrice_network_server_remote) - -target_include_directories(libcockatrice_network_client_abstract PUBLIC .) - -target_link_libraries( - libcockatrice_network_client_abstract - PUBLIC ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::WebSockets libcockatrice_protocol - libcockatrice_network_server_remote -) diff --git a/libcockatrice_network/libcockatrice/network/client/local/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/client/local/CMakeLists.txt deleted file mode 100644 index d12a324dc..000000000 --- a/libcockatrice_network/libcockatrice/network/client/local/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS local_client.h) - -set(SOURCES local_client.cpp) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library(libcockatrice_network_client_local STATIC ${MOC_SOURCES} ${SOURCES}) - -add_dependencies(libcockatrice_network_client_local libcockatrice_network_client_abstract) - -target_include_directories(libcockatrice_network_client_local PUBLIC .) - -target_link_libraries( - libcockatrice_network_client_local - PUBLIC ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::WebSockets - libcockatrice_network_client_abstract -) diff --git a/libcockatrice_network/libcockatrice/network/client/local/local_client.cpp b/libcockatrice_network/libcockatrice/network/client/local/local_client.cpp deleted file mode 100644 index eefa3a2f3..000000000 --- a/libcockatrice_network/libcockatrice/network/client/local/local_client.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "local_client.h" - -#include "../../server/local/local_server_interface.h" - -#include -#include - -LocalClient::LocalClient(LocalServerInterface *_lsi, - const QString &_playerName, - const QString &_clientId, - QObject *parent) - : AbstractClient(parent), lsi(_lsi) -{ - connect(lsi, &LocalServerInterface::itemToClient, this, &LocalClient::itemFromServer); - - userName = _playerName; - - Command_Login loginCmd; - loginCmd.set_user_name(_playerName.toStdString()); - loginCmd.set_clientid(_clientId.toStdString()); - sendCommand(prepareSessionCommand(loginCmd)); - - Command_JoinRoom joinCmd; - joinCmd.set_room_id(0); - sendCommand(prepareSessionCommand(joinCmd)); -} - -LocalClient::~LocalClient() -{ -} - -void LocalClient::sendCommandContainer(const CommandContainer &cont) -{ - qCDebug(LocalClientLog).noquote() << userName << "OUT" << getSafeDebugString(cont); - - lsi->itemFromClient(cont); -} - -void LocalClient::itemFromServer(const ServerMessage &item) -{ - qCDebug(LocalClientLog).noquote() << userName << "IN" << getSafeDebugString(item); - - processProtocolItem(item); -} diff --git a/libcockatrice_network/libcockatrice/network/client/local/local_client.h b/libcockatrice_network/libcockatrice/network/client/local/local_client.h deleted file mode 100644 index e8c5330ac..000000000 --- a/libcockatrice_network/libcockatrice/network/client/local/local_client.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file local_client.h - * @ingroup Client - * @brief TODO: Document this. - */ - -#ifndef LOCALCLIENT_H -#define LOCALCLIENT_H - -#include "../abstract/abstract_client.h" - -#include - -inline Q_LOGGING_CATEGORY(LocalClientLog, "local_client"); - -class LocalServerInterface; - -class LocalClient : public AbstractClient -{ - Q_OBJECT -private: - LocalServerInterface *lsi; - -public: - LocalClient(LocalServerInterface *_lsi, - const QString &_playerName, - const QString &_clientId, - QObject *parent = nullptr); - ~LocalClient() override; - - void sendCommandContainer(const CommandContainer &cont) override; -private slots: - void itemFromServer(const ServerMessage &item); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/client/remote/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/client/remote/CMakeLists.txt deleted file mode 100644 index 0548700e4..000000000 --- a/libcockatrice_network/libcockatrice/network/client/remote/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS remote_client.h) - -set(SOURCES remote_client.cpp) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library(libcockatrice_network_client_remote STATIC ${MOC_SOURCES} ${SOURCES}) - -add_dependencies(libcockatrice_network_client_remote libcockatrice_network_client_abstract libcockatrice_protocol) - -target_include_directories(libcockatrice_network_client_remote PUBLIC .) - -target_link_libraries( - libcockatrice_network_client_remote - PUBLIC ${COCKATRICE_QT_VERSION_NAME}::Network ${COCKATRICE_QT_VERSION_NAME}::WebSockets - libcockatrice_network_client_abstract libcockatrice_interfaces libcockatrice_utility libcockatrice_protocol -) diff --git a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp deleted file mode 100644 index 3e3ec889d..000000000 --- a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp +++ /dev/null @@ -1,739 +0,0 @@ -#include "remote_client.h" - -#include "version_string.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const unsigned int protocolVersion = 14; - -RemoteClient::RemoteClient(QObject *parent, INetworkSettingsProvider *_networkSettingsProvider) - : AbstractClient(parent), networkSettingsProvider(_networkSettingsProvider), timeRunning(0), lastDataReceived(0), - messageInProgress(false), handshakeStarted(false), usingWebSocket(false), messageLength(0), hashedPassword() -{ - - clearNewClientFeatures(); - maxTimeout = networkSettingsProvider->getTimeOut(); - int keepalive = networkSettingsProvider->getKeepAlive(); - timer = new QTimer(this); - timer->setInterval(keepalive * 1000); - connect(timer, &QTimer::timeout, this, &RemoteClient::ping); - - socket = new QTcpSocket(this); - socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); - connect(socket, &QTcpSocket::connected, this, &RemoteClient::slotConnected); - connect(socket, &QTcpSocket::readyRead, this, &RemoteClient::readData); - - connect(socket, &QTcpSocket::errorOccurred, this, &RemoteClient::slotSocketError); - - websocket = new QWebSocket(QString(), QWebSocketProtocol::VersionLatest, this); - connect(websocket, &QWebSocket::binaryMessageReceived, this, &RemoteClient::websocketMessageReceived); - connect(websocket, &QWebSocket::connected, this, &RemoteClient::slotConnected); - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - connect(websocket, &QWebSocket::errorOccurred, this, &RemoteClient::slotWebSocketError); -#else - connect(websocket, qOverload(&QWebSocket::error), this, - &RemoteClient::slotWebSocketError); -#endif - - connect(this, &RemoteClient::serverIdentificationEventReceived, this, - &RemoteClient::processServerIdentificationEvent); - connect(this, &RemoteClient::connectionClosedEventReceived, this, &RemoteClient::processConnectionClosedEvent); - connect(this, &RemoteClient::sigConnectToServer, this, &RemoteClient::doConnectToServer); - connect(this, &RemoteClient::sigDisconnectFromServer, this, &RemoteClient::doDisconnectFromServer); - connect(this, &RemoteClient::sigRegisterToServer, this, &RemoteClient::doRegisterToServer); - connect(this, &RemoteClient::sigActivateToServer, this, &RemoteClient::doActivateToServer); - connect(this, &RemoteClient::sigRequestForgotPasswordToServer, this, - &RemoteClient::doRequestForgotPasswordToServer); - connect(this, &RemoteClient::sigSubmitForgotPasswordResetToServer, this, - &RemoteClient::doSubmitForgotPasswordResetToServer); - connect(this, &RemoteClient::sigSubmitForgotPasswordChallengeToServer, this, - &RemoteClient::doSubmitForgotPasswordChallengeToServer); -} - -RemoteClient::~RemoteClient() -{ - doDisconnectFromServer(); - thread()->quit(); -} - -void RemoteClient::slotSocketError(QAbstractSocket::SocketError /*error*/) -{ - QString errorString = socket->errorString(); - doDisconnectFromServer(); - emit socketError(errorString); -} - -void RemoteClient::slotWebSocketError(QAbstractSocket::SocketError /*error*/) -{ - - QString errorString = websocket->errorString(); - if (getStatus() != ClientStatus::StatusDisconnected) { - doDisconnectFromServer(); - emit socketError(errorString); - } -} - -void RemoteClient::slotConnected() -{ - timeRunning = lastDataReceived = 0; - timer->start(); - - if (!usingWebSocket) { - // dirty hack to be compatible with v14 server - sendCommandContainer(CommandContainer()); - getNewCmdId(); - // end of hack - } -} - -void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentification &event) -{ - if (event.protocol_version() != protocolVersion) { - emit protocolVersionMismatch(protocolVersion, event.protocol_version()); - setStatus(StatusDisconnecting); - return; - } - serverSupportsPasswordHash = event.server_options() & Event_ServerIdentification::SupportsPasswordHash; - - if (getStatus() == StatusRequestingForgotPassword) { - Command_ForgotPasswordRequest cmdForgotPasswordRequest; - cmdForgotPasswordRequest.set_user_name(userName.toStdString()); - cmdForgotPasswordRequest.set_clientid(getSrvClientID(lastHostname).toStdString()); - PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordRequest); - connect(pend, &PendingCommand::finished, this, &RemoteClient::requestForgotPasswordResponse); - sendCommand(pend); - return; - } - - if (getStatus() == StatusSubmitForgotPasswordReset) { - Command_ForgotPasswordReset cmdForgotPasswordReset; - cmdForgotPasswordReset.set_user_name(userName.toStdString()); - cmdForgotPasswordReset.set_clientid(getSrvClientID(lastHostname).toStdString()); - cmdForgotPasswordReset.set_token(token.toStdString()); - if (!password.isEmpty() && serverSupportsPasswordHash) { - auto passwordSalt = PasswordHasher::generateRandomSalt(); - hashedPassword = PasswordHasher::computeHash(password, passwordSalt); - cmdForgotPasswordReset.set_hashed_new_password(hashedPassword.toStdString()); - } else if (!password.isEmpty()) { - qCWarning(RemoteClientLog) << "using plain text password to reset password"; - cmdForgotPasswordReset.set_new_password(password.toStdString()); - } - PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordReset); - connect(pend, &PendingCommand::finished, this, &RemoteClient::submitForgotPasswordResetResponse); - sendCommand(pend); - return; - } - - if (getStatus() == StatusSubmitForgotPasswordChallenge) { - Command_ForgotPasswordChallenge cmdForgotPasswordChallenge; - cmdForgotPasswordChallenge.set_user_name(userName.toStdString()); - cmdForgotPasswordChallenge.set_clientid(getSrvClientID(lastHostname).toStdString()); - cmdForgotPasswordChallenge.set_email(email.toStdString()); - PendingCommand *pend = prepareSessionCommand(cmdForgotPasswordChallenge); - connect(pend, &PendingCommand::finished, this, &RemoteClient::submitForgotPasswordChallengeResponse); - sendCommand(pend); - return; - } - - if (getStatus() == StatusRegistering) { - Command_Register cmdRegister; - cmdRegister.set_user_name(userName.toStdString()); - if (!password.isEmpty() && serverSupportsPasswordHash) { - auto passwordSalt = PasswordHasher::generateRandomSalt(); - hashedPassword = PasswordHasher::computeHash(password, passwordSalt); - cmdRegister.set_hashed_password(hashedPassword.toStdString()); - } else if (!password.isEmpty()) { - qCWarning(RemoteClientLog) << "using plain text password to register"; - cmdRegister.set_password(password.toStdString()); - } - cmdRegister.set_email(email.toStdString()); - cmdRegister.set_country(country.toStdString()); - cmdRegister.set_real_name(realName.toStdString()); - cmdRegister.set_clientid(getSrvClientID(lastHostname).toStdString()); - PendingCommand *pend = prepareSessionCommand(cmdRegister); - connect(pend, &PendingCommand::finished, this, &RemoteClient::registerResponse); - sendCommand(pend); - - return; - } - - if (getStatus() == StatusActivating) { - Command_Activate cmdActivate; - cmdActivate.set_user_name(userName.toStdString()); - cmdActivate.set_token(token.toStdString()); - cmdActivate.set_clientid(getSrvClientID(lastHostname).toStdString()); - - PendingCommand *pend = prepareSessionCommand(cmdActivate); - connect(pend, &PendingCommand::finished, this, &RemoteClient::activateResponse); - sendCommand(pend); - - return; - } - - doLogin(); -} - -void RemoteClient::doRequestPasswordSalt() -{ - setStatus(StatusGettingPasswordSalt); - Command_RequestPasswordSalt cmdRqSalt; - cmdRqSalt.set_user_name(userName.toStdString()); - - PendingCommand *pend = prepareSessionCommand(cmdRqSalt); - connect(pend, &PendingCommand::finished, this, &RemoteClient::passwordSaltResponse); - sendCommand(pend); -} - -Command_Login RemoteClient::generateCommandLogin() -{ - Command_Login cmdLogin; - cmdLogin.set_user_name(userName.toStdString()); - cmdLogin.set_clientid(getSrvClientID(lastHostname).toStdString()); - cmdLogin.set_clientver(VERSION_STRING); - - if (!clientFeatures.isEmpty()) { - QMap::iterator i; - for (i = clientFeatures.begin(); i != clientFeatures.end(); ++i) - cmdLogin.add_clientfeatures(i.key().toStdString().c_str()); - } - - return cmdLogin; -} - -void RemoteClient::doLogin() -{ - if (!password.isEmpty() && serverSupportsPasswordHash) { - // TODO store and log in using stored hashed password - if (hashedPassword.isEmpty()) { - doRequestPasswordSalt(); // ask salt to create hashedPassword, then log in - } else { - doHashedLogin(); // log in using hashed password instead - } - } else { - // TODO add setting for client to reject unhashed logins - setStatus(StatusLoggingIn); - Command_Login cmdLogin = generateCommandLogin(); - if (!password.isEmpty()) { - qCWarning(RemoteClientLog) << "using plain text password to log in"; - cmdLogin.set_password(password.toStdString()); - } - - PendingCommand *pend = prepareSessionCommand(cmdLogin); - connect(pend, &PendingCommand::finished, this, &RemoteClient::loginResponse); - sendCommand(pend); - } -} - -void RemoteClient::doHashedLogin() -{ - setStatus(StatusLoggingIn); - Command_Login cmdLogin = generateCommandLogin(); - - cmdLogin.set_hashed_password(hashedPassword.toStdString()); - - PendingCommand *pend = prepareSessionCommand(cmdLogin); - connect(pend, &PendingCommand::finished, this, &RemoteClient::loginResponse); - sendCommand(pend); -} - -void RemoteClient::processConnectionClosedEvent(const Event_ConnectionClosed & /*event*/) -{ - doDisconnectFromServer(); -} - -void RemoteClient::passwordSaltResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - const Response_PasswordSalt &resp = response.GetExtension(Response_PasswordSalt::ext); - auto passwordSalt = QString::fromStdString(resp.password_salt()); - if (passwordSalt.isEmpty()) { // the server does not recognize the user but allows them to enter unregistered - password.clear(); // the password will not be used - doLogin(); - } else { - hashedPassword = PasswordHasher::computeHash(password, passwordSalt); - doHashedLogin(); - } - } else if (response.response_code() != Response::RespNotConnected) { - emit loginError(response.response_code(), {}, 0, {}); - } -} - -void RemoteClient::loginResponse(const Response &response) -{ - const Response_Login &resp = response.GetExtension(Response_Login::ext); - - QString possibleMissingFeatures; - if (resp.missing_features_size() > 0) { - for (int i = 0; i < resp.missing_features_size(); ++i) - possibleMissingFeatures.append("," + QString::fromStdString(resp.missing_features(i))); - } - - if (response.response_code() == Response::RespOk) { - setStatus(StatusLoggedIn); - emit userInfoChanged(resp.user_info()); - - QList buddyList; - for (int i = resp.buddy_list_size() - 1; i >= 0; --i) - buddyList.append(resp.buddy_list(i)); - emit buddyListReceived(buddyList); - - QList ignoreList; - for (int i = resp.ignore_list_size() - 1; i >= 0; --i) - ignoreList.append(resp.ignore_list(i)); - emit ignoreListReceived(ignoreList); - - if (newMissingFeatureFound(possibleMissingFeatures) && resp.missing_features_size() > 0 && - networkSettingsProvider->getNotifyAboutUpdates()) { - networkSettingsProvider->setKnownMissingFeatures(possibleMissingFeatures); - emit notifyUserAboutUpdate(); - } - - } else if (response.response_code() != Response::RespNotConnected) { - QList missingFeatures; - if (resp.missing_features_size() > 0) { - for (int i = 0; i < resp.missing_features_size(); ++i) - missingFeatures << QString::fromStdString(resp.missing_features(i)); - } - emit loginError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), - static_cast(resp.denied_end_time()), missingFeatures); - setStatus(StatusDisconnecting); - } -} - -void RemoteClient::registerResponse(const Response &response) -{ - const Response_Register &resp = response.GetExtension(Response_Register::ext); - switch (response.response_code()) { - case Response::RespRegistrationAccepted: - emit registerAccepted(); - doLogin(); - break; - case Response::RespRegistrationAcceptedNeedsActivation: - emit registerAcceptedNeedsActivate(); - doLogin(); - break; - case Response::RespNotConnected: - // this response is created by the client from doDisconnectFromServer, do not call it again! - emit registerError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), - static_cast(resp.denied_end_time())); - break; - default: - emit registerError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), - static_cast(resp.denied_end_time())); - setStatus(StatusDisconnecting); - doDisconnectFromServer(); - break; - } -} - -void RemoteClient::activateResponse(const Response &response) -{ - if (response.response_code() == Response::RespActivationAccepted) { - emit activateAccepted(); - - doLogin(); - } else { - emit activateError(); - } -} - -void RemoteClient::readData() -{ - lastDataReceived = timeRunning; - QByteArray data = socket->readAll(); - - inputBuffer.append(data); - - do { - if (!messageInProgress) { - if (inputBuffer.size() >= 4) { - // dirty hack to be compatible with v14 server that sends 60 bytes of garbage at the beginning - if (!handshakeStarted) { - handshakeStarted = true; - if (inputBuffer.startsWith(" 3001000 - auto size = static_cast(cont.ByteSizeLong()); -#else - auto size = static_cast(cont.ByteSize()); -#endif - - qCDebug(RemoteClientLog).noquote() << "OUT" << getSafeDebugString(cont); - - QByteArray buf; - bool ok; - if (usingWebSocket) { - buf.resize(size); - ok = cont.SerializeToArray(buf.data(), size); - if (ok) { - websocket->sendBinaryMessage(buf); - } - } else { - buf.resize(size + 4); - ok = cont.SerializeToArray(buf.data() + 4, size); - if (ok) { - buf.data()[3] = (unsigned char)size; - buf.data()[2] = (unsigned char)(size >> 8); - buf.data()[1] = (unsigned char)(size >> 16); - buf.data()[0] = (unsigned char)(size >> 24); - - socket->write(buf); - } - } - if (!ok) { - qCDebug(RemoteClientLog) << "transmit error!"; - } -} - -void RemoteClient::connectToHost(const QString &hostname, unsigned int port) -{ - usingWebSocket = port == 443 || port == 80 || port == 4748 || port == 8080; - if (usingWebSocket) { - QUrl url(QString("%1://%2:%3/servatrice").arg(port == 443 ? "wss" : "ws").arg(hostname).arg(port)); - websocket->open(url); - } else { - socket->connectToHost(hostname, static_cast(port)); - } -} - -void RemoteClient::doConnectToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password) -{ - doDisconnectFromServer(); - - userName = _userName; - password = _password; - lastHostname = hostname; - lastPort = port; - hashedPassword.clear(); - - connectToHost(hostname, port); - setStatus(StatusConnecting); -} - -void RemoteClient::doRegisterToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password, - const QString &_email, - const QString &_country, - const QString &_realname) -{ - doDisconnectFromServer(); - - userName = _userName; - password = _password; - email = _email; - country = _country; - realName = _realname; - lastHostname = hostname; - lastPort = port; - hashedPassword.clear(); - - connectToHost(hostname, port); - setStatus(StatusRegistering); -} - -void RemoteClient::doActivateToServer(const QString &_token) -{ - doDisconnectFromServer(); - - token = _token.trimmed(); - - connectToHost(lastHostname, lastPort); - setStatus(StatusActivating); -} - -void RemoteClient::doDisconnectFromServer() -{ - timer->stop(); - - messageInProgress = false; - handshakeStarted = false; - messageLength = 0; - - QList pc = pendingCommands.values(); - for (const auto &i : pc) { - Response response; - response.set_response_code(Response::RespNotConnected); - response.set_cmd_id(i->getCommandContainer().cmd_id()); - i->processResponse(response); - - delete i; - } - pendingCommands.clear(); - - setStatus(StatusDisconnected); - if (websocket->isValid()) - websocket->close(); - socket->close(); -} - -void RemoteClient::ping() -{ - QMutableMapIterator i(pendingCommands); - while (i.hasNext()) { - PendingCommand *pend = i.next().value(); - if (pend->tick() > maxTimeout) { - i.remove(); - pend->deleteLater(); - } - } - - int maxTime = timeRunning - lastDataReceived; - emit maxPingTime(maxTime, maxTimeout); - if (maxTime >= maxTimeout) { - disconnectFromServer(); - emit serverTimeout(); - } else { - sendCommand(prepareSessionCommand(Command_Ping())); - ++timeRunning; - } -} - -void RemoteClient::connectToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password) -{ - emit sigConnectToServer(hostname, port, _userName, _password); -} - -void RemoteClient::registerToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password, - const QString &_email, - const QString &_country, - const QString &_realname) -{ - emit sigRegisterToServer(hostname, port, _userName, _password, _email, _country, _realname); -} - -void RemoteClient::activateToServer(const QString &_token) -{ - emit sigActivateToServer(_token.trimmed()); -} - -void RemoteClient::disconnectFromServer() -{ - emit sigDisconnectFromServer(); -} - -QString RemoteClient::getSrvClientID(const QString &_hostname) -{ - QString srvClientID = networkSettingsProvider->getClientID(); - QHostInfo hostInfo = QHostInfo::fromName(_hostname); - if (!hostInfo.error()) { - QHostAddress hostAddress = hostInfo.addresses().first(); - srvClientID += hostAddress.toString(); - } else { - qCWarning(RemoteClientLog) << "ClientID generation host lookup failure [" << hostInfo.errorString() << "]"; - srvClientID += _hostname; - } - QString uniqueServerClientID = - QCryptographicHash::hash(srvClientID.toUtf8(), QCryptographicHash::Sha1).toHex().right(15); - return uniqueServerClientID; -} - -bool RemoteClient::newMissingFeatureFound(const QString &_serversMissingFeatures) -{ - bool newMissingFeature = false; - QStringList serversMissingFeaturesList = _serversMissingFeatures.split(","); - for (const QString &feature : serversMissingFeaturesList) { - if (!feature.isEmpty()) { - if (!networkSettingsProvider->getKnownMissingFeatures().contains(feature)) - return true; - } - } - return newMissingFeature; -} - -void RemoteClient::clearNewClientFeatures() -{ - QString newKnownMissingFeatures; - QStringList existingKnownMissingFeatures = networkSettingsProvider->getKnownMissingFeatures().split(","); - for (const QString &existingKnownFeature : existingKnownMissingFeatures) { - if (!existingKnownFeature.isEmpty()) { - if (!clientFeatures.contains(existingKnownFeature)) - newKnownMissingFeatures.append("," + existingKnownFeature); - } - } - networkSettingsProvider->setKnownMissingFeatures(newKnownMissingFeatures); -} - -void RemoteClient::requestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName) -{ - emit sigRequestForgotPasswordToServer(hostname, port, _userName); -} - -void RemoteClient::submitForgotPasswordResetToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_token, - const QString &_newpassword) -{ - emit sigSubmitForgotPasswordResetToServer(hostname, port, _userName, _token.trimmed(), _newpassword); -} - -void RemoteClient::doRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName) -{ - doDisconnectFromServer(); - - userName = _userName; - lastHostname = hostname; - lastPort = port; - - connectToHost(lastHostname, lastPort); - setStatus(StatusRequestingForgotPassword); -} - -void RemoteClient::requestForgotPasswordResponse(const Response &response) -{ - const Response_ForgotPasswordRequest &resp = response.GetExtension(Response_ForgotPasswordRequest::ext); - if (response.response_code() == Response::RespOk) { - if (resp.challenge_email()) { - emit sigPromptForForgotPasswordChallenge(); - } else - emit sigPromptForForgotPasswordReset(); - } else - emit sigForgotPasswordError(); - - doDisconnectFromServer(); -} - -void RemoteClient::doSubmitForgotPasswordResetToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_token, - const QString &_newpassword) -{ - doDisconnectFromServer(); - - userName = _userName; - lastHostname = hostname; - lastPort = port; - token = _token.trimmed(); - password = _newpassword; - hashedPassword.clear(); - - connectToHost(lastHostname, lastPort); - setStatus(StatusSubmitForgotPasswordReset); -} - -void RemoteClient::submitForgotPasswordResetResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - emit sigForgotPasswordSuccess(); - } else - emit sigForgotPasswordError(); - - doDisconnectFromServer(); -} - -void RemoteClient::submitForgotPasswordChallengeToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_email) -{ - emit sigSubmitForgotPasswordChallengeToServer(hostname, port, _userName, _email); -} - -void RemoteClient::doSubmitForgotPasswordChallengeToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_email) -{ - doDisconnectFromServer(); - - userName = _userName; - lastHostname = hostname; - lastPort = port; - email = _email; - - connectToHost(lastHostname, lastPort); - setStatus(StatusSubmitForgotPasswordChallenge); -} - -void RemoteClient::submitForgotPasswordChallengeResponse(const Response &response) -{ - if (response.response_code() == Response::RespOk) { - emit sigPromptForForgotPasswordReset(); - } else - emit sigForgotPasswordError(); - - doDisconnectFromServer(); -} diff --git a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.h b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.h deleted file mode 100644 index 15e3e8ef5..000000000 --- a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.h +++ /dev/null @@ -1,157 +0,0 @@ -/** - * @file remote_client.h - * @ingroup Client - * @brief TODO: Document this. - */ - -#ifndef REMOTECLIENT_H -#define REMOTECLIENT_H - -#include "../abstract/abstract_client.h" - -#include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(RemoteClientLog, "remote_client"); - -class QTimer; - -class RemoteClient : public AbstractClient -{ - Q_OBJECT -signals: - void serverTimeout(); - void loginError(Response::ResponseCode resp, QString reasonStr, quint32 endTime, QList missingFeatures); - void registerError(Response::ResponseCode resp, QString reasonStr, quint32 endTime); - void activateError(); - void socketError(const QString &errorString); - void protocolVersionMismatch(int clientVersion, int serverVersion); - void - sigConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); - void sigRegisterToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password, - const QString &_email, - const QString &_country, - const QString &_realname); - void sigActivateToServer(const QString &_token); - void sigDisconnectFromServer(); - void notifyUserAboutUpdate(); - void sigRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName); - void sigForgotPasswordSuccess(); - void sigForgotPasswordError(); - void sigPromptForForgotPasswordReset(); - void sigSubmitForgotPasswordResetToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_token, - const QString &_newpassword); - void sigPromptForForgotPasswordChallenge(); - void sigSubmitForgotPasswordChallengeToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_email); -private slots: - void slotConnected(); - void readData(); - void websocketMessageReceived(const QByteArray &message); - void slotSocketError(QAbstractSocket::SocketError error); - void slotWebSocketError(QAbstractSocket::SocketError error); - void ping(); - void processServerIdentificationEvent(const Event_ServerIdentification &event); - void processConnectionClosedEvent(const Event_ConnectionClosed &event); - void passwordSaltResponse(const Response &response); - void loginResponse(const Response &response); - void registerResponse(const Response &response); - void activateResponse(const Response &response); - void - doConnectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); - void doRegisterToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password, - const QString &_email, - const QString &_country, - const QString &_realname); - void doRequestPasswordSalt(); - void doLogin(); - void doHashedLogin(); - Command_Login generateCommandLogin(); - void doDisconnectFromServer(); - void doActivateToServer(const QString &_token); - void doRequestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName); - void requestForgotPasswordResponse(const Response &response); - void doSubmitForgotPasswordResetToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_token, - const QString &_newpassword); - void submitForgotPasswordResetResponse(const Response &response); - void doSubmitForgotPasswordChallengeToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_email); - void submitForgotPasswordChallengeResponse(const Response &response); - -private: - INetworkSettingsProvider *networkSettingsProvider; - int maxTimeout; - int timeRunning, lastDataReceived; - QByteArray inputBuffer; - bool messageInProgress; - bool handshakeStarted; - bool usingWebSocket; - int messageLength; - QTimer *timer; - QTcpSocket *socket; - QWebSocket *websocket; - QString lastHostname; - unsigned int lastPort; - QString hashedPassword; - - QString getSrvClientID(const QString &_hostname); - bool newMissingFeatureFound(const QString &_serversMissingFeatures); - void clearNewClientFeatures(); - void connectToHost(const QString &hostname, unsigned int port); - -protected slots: - void sendCommandContainer(const CommandContainer &cont) override; - -public: - explicit RemoteClient(QObject *parent = nullptr, INetworkSettingsProvider *networkSettingsProvider = nullptr); - ~RemoteClient() override; - QString peerName() const - { - if (usingWebSocket) { - return websocket->peerName(); - } else { - return socket->peerName(); - } - } - void - connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); - void registerToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_password, - const QString &_email, - const QString &_country, - const QString &_realname); - void activateToServer(const QString &_token); - void disconnectFromServer(); - void requestForgotPasswordToServer(const QString &hostname, unsigned int port, const QString &_userName); - void submitForgotPasswordResetToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_token, - const QString &_newpassword); - void submitForgotPasswordChallengeToServer(const QString &hostname, - unsigned int port, - const QString &_userName, - const QString &_email); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/server/CMakeLists.txt deleted file mode 100644 index cbb717ad8..000000000 --- a/libcockatrice_network/libcockatrice/network/server/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -add_subdirectory(local) -add_subdirectory(remote) - -add_library(libcockatrice_network_server INTERFACE) - -target_include_directories(libcockatrice_network_server INTERFACE .) - -target_link_libraries( - libcockatrice_network_server INTERFACE ${COCKATRICE_QT_MODULES} libcockatrice_network_server_local - libcockatrice_network_server_remote -) diff --git a/libcockatrice_network/libcockatrice/network/server/local/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/server/local/CMakeLists.txt deleted file mode 100644 index 80fb379a4..000000000 --- a/libcockatrice_network/libcockatrice/network/server/local/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS local_server.h local_server_interface.h) - -set(SOURCES local_server.cpp local_server_interface.cpp) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library(libcockatrice_network_server_local STATIC ${MOC_SOURCES} ${SOURCES}) - -add_dependencies(libcockatrice_network_server_local libcockatrice_protocol) - -target_include_directories(libcockatrice_network_server_local PUBLIC .) - -target_link_libraries(libcockatrice_network_server_local PUBLIC ${COCKATRICE_QT_MODULES} libcockatrice_protocol) diff --git a/libcockatrice_network/libcockatrice/network/server/local/local_server.cpp b/libcockatrice_network/libcockatrice/network/server/local/local_server.cpp deleted file mode 100644 index 8f9d82aa4..000000000 --- a/libcockatrice_network/libcockatrice/network/server/local/local_server.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "local_server.h" - -#include "local_server_interface.h" - -#include <../remote/server_room.h> - -LocalServer::LocalServer(QObject *parent) : Server(parent) -{ - setDatabaseInterface(new LocalServer_DatabaseInterface(this)); - addRoom(new Server_Room(0, 0, QString(), QString(), QString(), QString(), false, QString(), QStringList(), this)); -} - -LocalServer::~LocalServer() -{ - // LocalServer is single threaded so it doesn't need locks on this - for (auto *client : clients) { - client->prepareDestroy(); - } - - prepareDestroy(); -} - -LocalServerInterface *LocalServer::newConnection() -{ - LocalServerInterface *lsi = new LocalServerInterface(this, getDatabaseInterface()); - addClient(lsi); - return lsi; -} - -LocalServer_DatabaseInterface::LocalServer_DatabaseInterface(LocalServer *_localServer) - : Server_DatabaseInterface(_localServer), localServer(_localServer) -{ -} - -ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name, bool /*withId*/) -{ - ServerInfo_User result; - result.set_name(name.toStdString()); - return result; -} - -AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, - const QString & /* user */, - const QString & /* password */, - const QString & /* clientId */, - QString & /* reasonStr */, - int & /* banSecondsLeft */, - bool /* passwordNeedsHash */) -{ - return UnknownUser; -} diff --git a/libcockatrice_network/libcockatrice/network/server/local/local_server.h b/libcockatrice_network/libcockatrice/network/server/local/local_server.h deleted file mode 100644 index 70586f6c1..000000000 --- a/libcockatrice_network/libcockatrice/network/server/local/local_server.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file local_server.h - * @ingroup Server - * @brief TODO: Document this. - */ - -#ifndef LOCALSERVER_H -#define LOCALSERVER_H - -#include <../remote/server.h> -#include <../remote/server_database_interface.h> - -class LocalServerInterface; - -class LocalServer : public Server -{ - Q_OBJECT -public: - explicit LocalServer(QObject *parent = nullptr); - ~LocalServer() override; - - LocalServerInterface *newConnection(); -}; - -class LocalServer_DatabaseInterface : public Server_DatabaseInterface -{ - Q_OBJECT -private: - LocalServer *localServer; - -protected: - ServerInfo_User getUserData(const QString &name, bool withId = false) override; - -public: - explicit LocalServer_DatabaseInterface(LocalServer *_localServer); - ~LocalServer_DatabaseInterface() override = default; - AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, - const QString &user, - const QString &password, - const QString &clientId, - QString &reasonStr, - int &secondsLeft, - bool passwordNeedsHash) override; - int getNextGameId() override - { - return localServer->getNextLocalGameId(); - } - int getNextReplayId() override - { - return -1; - } - int getActiveUserCount(QString /* connectionType */) override - { - return 0; - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/local/local_server_interface.h b/libcockatrice_network/libcockatrice/network/server/local/local_server_interface.h deleted file mode 100644 index 4410fd65c..000000000 --- a/libcockatrice_network/libcockatrice/network/server/local/local_server_interface.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file local_server_interface.h - * @ingroup Server - * @brief TODO: Document this. - */ - -#ifndef LOCALSERVERINTERFACE_H -#define LOCALSERVERINTERFACE_H - -#include <../remote/server_protocolhandler.h> - -class LocalServer; - -class LocalServerInterface : public Server_ProtocolHandler -{ - Q_OBJECT -public: - LocalServerInterface(LocalServer *_server, Server_DatabaseInterface *_databaseInterface); - ~LocalServerInterface() override; - - QString getAddress() const override - { - return QString(); - } - QString getConnectionType() const override - { - return "local"; - } - void transmitProtocolItem(const ServerMessage &item) override; -signals: - void itemToClient(const ServerMessage &item); -public slots: - void itemFromClient(const CommandContainer &item); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt b/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt deleted file mode 100644 index e883baa0d..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - game/server_abstract_participant.h - game/server_abstract_player.h - game/server_arrow.h - game/server_arrowtarget.h - game/server_card.h - game/server_cardzone.h - game/server_counter.h - game/server_game.h - game/server_player.h - game/server_spectator.h - server.h - server_abstractuserinterface.h - server_database_interface.h - server_protocolhandler.h - server_remoteuserinterface.h - server_response_containers.h - server_room.h - serverinfo_user_container.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_network_server_remote STATIC - ${MOC_SOURCES} - game/server_abstract_participant.cpp - game/server_abstract_player.cpp - game/server_arrow.cpp - game/server_arrowtarget.cpp - game/server_card.cpp - game/server_cardzone.cpp - game/server_counter.cpp - game/server_game.cpp - game/server_player.cpp - game/server_spectator.cpp - server.cpp - server_abstractuserinterface.cpp - server_database_interface.cpp - server_protocolhandler.cpp - server_remoteuserinterface.cpp - server_response_containers.cpp - server_room.cpp - serverinfo_user_container.cpp -) - -add_dependencies(libcockatrice_network_server_remote libcockatrice_protocol) - -target_include_directories(libcockatrice_network_server_remote PUBLIC .) - -# Make cockatrice_server depend on cockatrice_protocol -target_link_libraries( - libcockatrice_network_server_remote PUBLIC libcockatrice_protocol libcockatrice_utility libcockatrice_rng - libcockatrice_deck_list ${COCKATRICE_QT_MODULES} -) diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp deleted file mode 100644 index 493b8e966..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.cpp +++ /dev/null @@ -1,575 +0,0 @@ -#include "server_abstract_participant.h" - -#include "../server.h" -#include "../server_abstractuserinterface.h" -#include "../server_database_interface.h" -#include "../server_room.h" -#include "server_card.h" -#include "server_game.h" -#include "server_player.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Server_AbstractParticipant::Server_AbstractParticipant(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_userInterface) - : ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), pingTime(0), - playerId(_playerId), judge(_judge) -{ -} - -Server_AbstractParticipant::~Server_AbstractParticipant() = default; - -void Server_AbstractParticipant::removeFromGame() -{ - QMutexLocker locker(&playerMutex); - if (userInterface) { - userInterface->playerRemovedFromGame(game); - } -} - -bool Server_AbstractParticipant::updatePingTime() // returns true if ping time changed -{ - QMutexLocker locker(&playerMutex); - - int oldPingTime = pingTime; - if (userInterface) { - pingTime = userInterface->getLastCommandTime(); - } else { - pingTime = -1; - } - - return pingTime != oldPingTime; -} - -void Server_AbstractParticipant::getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo) -{ - result.set_player_id(playerId); - if (withUserInfo) { - copyUserInfo(*(result.mutable_user_info()), true); - } - result.set_spectator(spectator); - result.set_judge(judge); - result.set_ping_seconds(pingTime); - getPlayerProperties(result); -} - -void Server_AbstractParticipant::getPlayerProperties(ServerInfo_PlayerProperties & /*result*/) -{ -} - -Response::ResponseCode Server_AbstractParticipant::cmdLeaveGame(const Command_LeaveGame & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - game->removeParticipant(this, Event_Leave::USER_LEFT); - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdKickFromGame(const Command_KickFromGame &cmd, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - if ((game->getHostId() != playerId) && !(userInfo->user_level() & ServerInfo_User::IsModerator)) { - return Response::RespFunctionNotAllowed; - } - - if (!game->kickParticipant(cmd.player_id())) { - return Response::RespNameNotFound; - } - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdDeckSelect(const Command_DeckSelect & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardPlan(const Command_SetSideboardPlan & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetSideboardLock(const Command_SetSideboardLock & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdConcede(const Command_Concede & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdUnconcede(const Command_Unconcede & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode -Server_AbstractParticipant::cmdJudge(const Command_Judge &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - if (!judge) { - return Response::RespFunctionNotAllowed; - } - - auto *player = this->game->getPlayer(cmd.target_id()); - - ges.setForcedByJudge(playerId); - if (player == nullptr) { - return Response::RespContextError; - } - - for (int i = 0; i < cmd.game_command_size(); ++i) { - player->processGameCommand(cmd.game_command(i), rc, ges); - } - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdReadyStart(const Command_ReadyStart & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode -Server_AbstractParticipant::cmdGameSay(const Command_GameSay &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (spectator) { - /* Spectators can only talk if: - * (a) the game creator allows it - * (b) the spectator is a moderator/administrator - * (c) the spectator is a judge - */ - bool isModOrJudge = (userInfo->user_level() & (ServerInfo_User::IsModerator | ServerInfo_User::IsJudge)); - if (!isModOrJudge && !game->getSpectatorsCanTalk()) { - return Response::RespFunctionNotAllowed; - } - } - - if (!userInterface->addSaidMessageSize(static_cast(cmd.message().size()))) { - return Response::RespChatFlood; - } - Event_GameSay event; - event.set_message(cmd.message()); - ges.enqueueGameEvent(event, playerId); - - game->getRoom()->getServer()->getDatabaseInterface()->logMessage( - userInfo->id(), QString::fromStdString(userInfo->name()), QString::fromStdString(userInfo->address()), - textFromStdString(cmd.message()), Server_DatabaseInterface::MessageTargetGame, game->getGameId(), - game->getDescription()); - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdShuffle(const Command_Shuffle & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdMulligan(const Command_Mulligan & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdRollDie(const Command_RollDie & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) const -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdDrawCards(const Command_DrawCards & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdMoveCard(const Command_MoveCard & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdFlipCard(const Command_FlipCard & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdAttachCard(const Command_AttachCard & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdCreateToken(const Command_CreateToken & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdCreateArrow(const Command_CreateArrow & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdDeleteArrow(const Command_DeleteArrow & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetCardAttr(const Command_SetCardAttr & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetCardCounter(const Command_SetCardCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdIncCardCounter(const Command_IncCardCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdIncCounter(const Command_IncCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdCreateCounter(const Command_CreateCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetCounter(const Command_SetCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdDelCounter(const Command_DelCounter & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdNextTurn(const Command_NextTurn & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - if (!judge) { - return Response::RespFunctionNotAllowed; - } - - game->nextTurn(); - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdSetActivePhase(const Command_SetActivePhase &cmd, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - if (!judge) { - return Response::RespFunctionNotAllowed; - } - - game->setActivePhase(cmd.phase()); - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractParticipant::cmdDumpZone(const Command_DumpZone & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdRevealCards(const Command_RevealCards & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdChangeZoneProperties(const Command_ChangeZoneProperties & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode Server_AbstractParticipant::cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage &ges) -{ - if (!judge) { - if (spectator) { - return Response::RespFunctionNotAllowed; - } - - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - } - - bool reversedTurn = game->reverseTurnOrder(); - - Event_ReverseTurn event; - event.set_reversed(reversedTurn); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractParticipant::processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges) -{ - switch ((GameCommand::GameCommandType)getPbExtension(command)) { - case GameCommand::KICK_FROM_GAME: - return cmdKickFromGame(command.GetExtension(Command_KickFromGame::ext), rc, ges); - break; - case GameCommand::LEAVE_GAME: - return cmdLeaveGame(command.GetExtension(Command_LeaveGame::ext), rc, ges); - break; - case GameCommand::GAME_SAY: - return cmdGameSay(command.GetExtension(Command_GameSay::ext), rc, ges); - break; - case GameCommand::SHUFFLE: - return cmdShuffle(command.GetExtension(Command_Shuffle::ext), rc, ges); - break; - case GameCommand::MULLIGAN: - return cmdMulligan(command.GetExtension(Command_Mulligan::ext), rc, ges); - break; - case GameCommand::ROLL_DIE: - return cmdRollDie(command.GetExtension(Command_RollDie::ext), rc, ges); - break; - case GameCommand::DRAW_CARDS: - return cmdDrawCards(command.GetExtension(Command_DrawCards::ext), rc, ges); - break; - case GameCommand::UNDO_DRAW: - return cmdUndoDraw(command.GetExtension(Command_UndoDraw::ext), rc, ges); - break; - case GameCommand::FLIP_CARD: - return cmdFlipCard(command.GetExtension(Command_FlipCard::ext), rc, ges); - break; - case GameCommand::ATTACH_CARD: - return cmdAttachCard(command.GetExtension(Command_AttachCard::ext), rc, ges); - break; - case GameCommand::CREATE_TOKEN: - return cmdCreateToken(command.GetExtension(Command_CreateToken::ext), rc, ges); - break; - case GameCommand::CREATE_ARROW: - return cmdCreateArrow(command.GetExtension(Command_CreateArrow::ext), rc, ges); - break; - case GameCommand::DELETE_ARROW: - return cmdDeleteArrow(command.GetExtension(Command_DeleteArrow::ext), rc, ges); - break; - case GameCommand::SET_CARD_ATTR: - return cmdSetCardAttr(command.GetExtension(Command_SetCardAttr::ext), rc, ges); - break; - case GameCommand::SET_CARD_COUNTER: - return cmdSetCardCounter(command.GetExtension(Command_SetCardCounter::ext), rc, ges); - break; - case GameCommand::INC_CARD_COUNTER: - return cmdIncCardCounter(command.GetExtension(Command_IncCardCounter::ext), rc, ges); - break; - case GameCommand::READY_START: - return cmdReadyStart(command.GetExtension(Command_ReadyStart::ext), rc, ges); - break; - case GameCommand::CONCEDE: - return cmdConcede(command.GetExtension(Command_Concede::ext), rc, ges); - break; - case GameCommand::INC_COUNTER: - return cmdIncCounter(command.GetExtension(Command_IncCounter::ext), rc, ges); - break; - case GameCommand::CREATE_COUNTER: - return cmdCreateCounter(command.GetExtension(Command_CreateCounter::ext), rc, ges); - break; - case GameCommand::SET_COUNTER: - return cmdSetCounter(command.GetExtension(Command_SetCounter::ext), rc, ges); - break; - case GameCommand::DEL_COUNTER: - return cmdDelCounter(command.GetExtension(Command_DelCounter::ext), rc, ges); - break; - case GameCommand::NEXT_TURN: - return cmdNextTurn(command.GetExtension(Command_NextTurn::ext), rc, ges); - break; - case GameCommand::SET_ACTIVE_PHASE: - return cmdSetActivePhase(command.GetExtension(Command_SetActivePhase::ext), rc, ges); - break; - case GameCommand::DUMP_ZONE: - return cmdDumpZone(command.GetExtension(Command_DumpZone::ext), rc, ges); - break; - case GameCommand::REVEAL_CARDS: - return cmdRevealCards(command.GetExtension(Command_RevealCards::ext), rc, ges); - break; - case GameCommand::MOVE_CARD: - return cmdMoveCard(command.GetExtension(Command_MoveCard::ext), rc, ges); - break; - case GameCommand::SET_SIDEBOARD_PLAN: - return cmdSetSideboardPlan(command.GetExtension(Command_SetSideboardPlan::ext), rc, ges); - break; - case GameCommand::DECK_SELECT: - return cmdDeckSelect(command.GetExtension(Command_DeckSelect::ext), rc, ges); - break; - case GameCommand::SET_SIDEBOARD_LOCK: - return cmdSetSideboardLock(command.GetExtension(Command_SetSideboardLock::ext), rc, ges); - break; - case GameCommand::CHANGE_ZONE_PROPERTIES: - return cmdChangeZoneProperties(command.GetExtension(Command_ChangeZoneProperties::ext), rc, ges); - break; - case GameCommand::UNCONCEDE: - return cmdUnconcede(command.GetExtension(Command_Unconcede::ext), rc, ges); - break; - case GameCommand::JUDGE: - return cmdJudge(command.GetExtension(Command_Judge::ext), rc, ges); - break; - case GameCommand::REVERSE_TURN: - return cmdReverseTurn(command.GetExtension(Command_ReverseTurn::ext), rc, ges); - break; - default: - return Response::RespInvalidCommand; - } -} - -void Server_AbstractParticipant::sendGameEvent(const GameEventContainer &cont) -{ - QMutexLocker locker(&playerMutex); - - if (userInterface) { - userInterface->sendProtocolItem(cont); - } -} - -void Server_AbstractParticipant::setUserInterface(Server_AbstractUserInterface *_userInterface) -{ - playerMutex.lock(); - userInterface = _userInterface; - playerMutex.unlock(); - - pingTime = _userInterface ? 0 : -1; - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ping_seconds(pingTime); - - GameEventStorage ges; - ges.setGameEventContext(Context_ConnectionStateChanged()); - ges.enqueueGameEvent(event, playerId); - ges.sendToGame(game); -} - -void Server_AbstractParticipant::disconnectClient() -{ - bool isRegistered = userInfo->user_level() & ServerInfo_User::IsRegistered; - if (!isRegistered || spectator) { - game->removeParticipant(this, Event_Leave::USER_DISCONNECTED); - } else { - setUserInterface(nullptr); - } -} - -void Server_AbstractParticipant::getInfo(ServerInfo_Player *info, - Server_AbstractParticipant * /*recipient*/, - bool /* omniscient */, - bool withUserInfo) -{ - getProperties(*info->mutable_properties(), withUserInfo); -} diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h deleted file mode 100644 index a24fa5799..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_participant.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef ABSTRACT_PARTICIPANT_H -#define ABSTRACT_PARTICIPANT_H - -#include "../serverinfo_user_container.h" -#include "server_arrowtarget.h" - -#include -#include -#include - -class Server_Game; -class Server_AbstractUserInterface; -class ServerInfo_User; -class ServerInfo_Player; -class ServerInfo_PlayerProperties; -class GameEventContainer; -class GameEventStorage; -class ResponseContainer; -class GameCommand; - -class Command_KickFromGame; -class Command_LeaveGame; -class Command_GameSay; -class Command_Shuffle; -class Command_Mulligan; -class Command_RollDie; -class Command_DrawCards; -class Command_UndoDraw; -class Command_FlipCard; -class Command_AttachCard; -class Command_CreateToken; -class Command_CreateArrow; -class Command_DeleteArrow; -class Command_SetCardAttr; -class Command_SetCardCounter; -class Command_IncCardCounter; -class Command_ReadyStart; -class Command_Concede; -class Command_Unconcede; -class Command_Judge; -class Command_IncCounter; -class Command_CreateCounter; -class Command_SetCounter; -class Command_DelCounter; -class Command_NextTurn; -class Command_SetActivePhase; -class Command_DumpZone; -class Command_RevealCards; -class Command_ReverseTurn; -class Command_MoveCard; -class Command_SetSideboardPlan; -class Command_DeckSelect; -class Command_SetSideboardLock; -class Command_ChangeZoneProperties; - -class Server_AbstractParticipant : public Server_ArrowTarget, public ServerInfo_User_Container -{ - Q_OBJECT -protected: - Server_Game *game; - Server_AbstractUserInterface *userInterface; - int pingTime; - int playerId; - bool spectator; - bool judge; - virtual void getPlayerProperties(ServerInfo_PlayerProperties &result); - mutable QMutex playerMutex; - -public: - Server_AbstractParticipant(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_handler); - ~Server_AbstractParticipant() override; - virtual void prepareDestroy() - { - removeFromGame(); - } - void removeFromGame(); - Server_AbstractUserInterface *getUserInterface() const - { - return userInterface; - } - void setUserInterface(Server_AbstractUserInterface *_userInterface); - void disconnectClient(); - - int getPlayerId() const - { - return playerId; - } - bool isSpectator() const - { - return spectator; - } - bool isJudge() const - { - return judge; - } - Server_Game *getGame() const - { - return game; - } - int getPingTime() const - { - return pingTime; - } - bool updatePingTime(); - void getProperties(ServerInfo_PlayerProperties &result, bool withUserInfo); - - virtual Response::ResponseCode - cmdLeaveGame(const Command_LeaveGame &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdKickFromGame(const Command_KickFromGame &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdUnconcede(const Command_Unconcede &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode cmdJudge(const Command_Judge &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode cmdGameSay(const Command_GameSay &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges) const; - virtual Response::ResponseCode - cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges); - virtual Response::ResponseCode - cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges); - virtual Response::ResponseCode - cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, ResponseContainer &rc, GameEventStorage &ges); - - Response::ResponseCode processGameCommand(const GameCommand &command, ResponseContainer &rc, GameEventStorage &ges); - void sendGameEvent(const GameEventContainer &event); - - virtual void - getInfo(ServerInfo_Player *info, Server_AbstractParticipant *recipient, bool omniscient, bool withUserInfo); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp deleted file mode 100644 index 7c0437bf0..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.cpp +++ /dev/null @@ -1,1652 +0,0 @@ -#include "server_abstract_player.h" - -#include "server_arrow.h" -#include "server_card.h" -#include "server_cardzone.h" -#include "server_game.h" -#include "server_move_card_struct.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Server_AbstractPlayer::Server_AbstractPlayer(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_userInterface) - : Server_AbstractParticipant(_game, _playerId, _userInfo, _judge, _userInterface), conceded(false), deck(nullptr), - sideboardLocked(true), readyStart(false), nextCardId(0) -{ - spectator = false; -} - -Server_AbstractPlayer::~Server_AbstractPlayer() = default; - -void Server_AbstractPlayer::prepareDestroy() -{ - delete deck; - deck = nullptr; - - removeFromGame(); - clearZones(); - - deleteLater(); -} - -int Server_AbstractPlayer::newCardId() -{ - return nextCardId++; -} - -int Server_AbstractPlayer::newArrowId() const -{ - int id = 0; - for (Server_Arrow *a : arrows) { - if (a->getId() > id) { - id = a->getId(); - } - } - return id + 1; -} - -void Server_AbstractPlayer::setupZones() -{ - nextCardId = 0; -} - -void Server_AbstractPlayer::clearZones() -{ - for (Server_CardZone *zone : zones) { - delete zone; - } - zones.clear(); - - for (Server_Arrow *arrow : arrows) { - delete arrow; - } - arrows.clear(); -} - -void Server_AbstractPlayer::addZone(Server_CardZone *zone) -{ - zones.insert(zone->getName(), zone); -} - -void Server_AbstractPlayer::addArrow(Server_Arrow *arrow) -{ - arrows.insert(arrow->getId(), arrow); -} - -void Server_AbstractPlayer::updateArrowId(int id) -{ - auto *arrow = arrows.take(id); - arrows.insert(arrow->getId(), arrow); -} - -bool Server_AbstractPlayer::deleteArrow(int arrowId) -{ - Server_Arrow *arrow = arrows.value(arrowId, 0); - if (!arrow) { - return false; - } - arrows.remove(arrowId); - delete arrow; - return true; -} - -/** - * Creates the create token event. - * By default, will set event's name and color fields to empty if the token is face-down - */ -static Event_CreateToken -makeCreateTokenEvent(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, bool revealFacedownInfo = false) -{ - Event_CreateToken event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_face_down(card->getFaceDown()); - - if (!card->getFaceDown() || revealFacedownInfo) { - event.set_card_name(card->getName().toStdString()); - event.set_card_provider_id(card->getProviderId().toStdString()); - } - - event.set_color(card->getColor().toStdString()); - event.set_pt(card->getPT().toStdString()); - event.set_annotation(card->getAnnotation().toStdString()); - event.set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - event.set_x(xCoord); - event.set_y(yCoord); - return event; -} - -static Event_AttachCard makeAttachCardEvent(Server_Card *attachedCard, Server_Card *parentCard = nullptr) -{ - Event_AttachCard event; - event.set_start_zone(attachedCard->getZone()->getName().toStdString()); - event.set_card_id(attachedCard->getId()); - - if (parentCard) { - event.set_target_player_id(parentCard->getZone()->getPlayer()->getPlayerId()); - event.set_target_zone(parentCard->getZone()->getName().toStdString()); - event.set_target_card_id(parentCard->getId()); - } - - return event; -} - -/** - * Determines whether moving the card from startZone to targetZone should cause the card to be destroyed. - */ -static bool -shouldDestroyOnMove(const Server_Card *card, const Server_CardZone *startZone, const Server_CardZone *targetZone) -{ - if (!card->getDestroyOnZoneChange()) { - return false; - } - - if (startZone->getName() == targetZone->getName()) { - return false; - } - - // Allow tokens on the stack - if ((startZone->getName() == ZoneNames::TABLE || startZone->getName() == ZoneNames::STACK) && - (targetZone->getName() == ZoneNames::TABLE || targetZone->getName() == ZoneNames::STACK)) { - return false; - } - - return true; -} - -/** - * @brief Determines whether the moved card should be face-down - */ -static bool -shouldBeFaceDown(const MoveCardStruct &cardStruct, const Server_CardZone *startZone, const Server_CardZone *targetZone) -{ - if (!targetZone) { - return false; - } - - // being face-down only makes sense for public zones - if (targetZone->getType() != ServerInfo_Zone::PublicZone) { - return false; - } - - // face-down property in proto takes precedence - if (cardStruct.cardToMove->has_face_down()) { - return cardStruct.cardToMove->face_down(); - } - - // Default to keep face-down the same if zone didn't change. - // Compare using zone names because face-down is maintained when changing controllers. - if (startZone && startZone->getName() == targetZone->getName()) { - return cardStruct.card->getFaceDown(); - } - - return false; -} - -/** - * @brief Determines whether a set of moved cards is from the bottom of the deck - */ -static bool shouldBeFromTheBottom(const Server_CardZone *startZone, const std::set &cardsToMove) -{ - if (!startZone) { - return false; - } - - if (startZone->getName() != ZoneNames::DECK) { - return false; - } - - int movedCount = static_cast(cardsToMove.size()); - int tailStart = startZone->getCards().size() - movedCount; - if (tailStart <= 0) { // if the entire deck is moved it should not be considered from the bottom - return false; - } - - // check if the move is a contiguous block at the end of the deck, fail fast when not - int expectedPosition = tailStart; - for (const auto &card : cardsToMove) { - if (card.position != expectedPosition) { - return false; - } - ++expectedPosition; - } - - return true; -} - -Response::ResponseCode Server_AbstractPlayer::moveCard(GameEventStorage &ges, - Server_CardZone *startzone, - const QList &_cards, - Server_CardZone *targetzone, - int xCoord, - int yCoord, - bool fixFreeSpaces, - bool undoingDraw, - bool isReversed) -{ - // Disallow controller change to other zones than the table. - if (((targetzone->getType() != ServerInfo_Zone::PublicZone) || !targetzone->hasCoords()) && - (startzone->getPlayer() != targetzone->getPlayer()) && !judge) { - return Response::RespContextError; - } - - if (!targetzone->hasCoords()) { - yCoord = 0; - if (xCoord <= -1) { - xCoord = targetzone->getCards().size(); - } - } - - std::set cardsToMove; - QSet cardIdsToMove; - for (auto _card : _cards) { - // The same card being moved twice would lead to undefined behaviour. - if (cardIdsToMove.contains(_card->card_id())) { - continue; - } - cardIdsToMove.insert(_card->card_id()); - - // Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still. - int position; - Server_Card *card = startzone->getCard(_card->card_id(), &position); - if (!card) { - return Response::RespNameNotFound; - } - - // do not allow attached cards to move around on the table - if (card->getParentCard() && targetzone->getName() == ZoneNames::TABLE) { - continue; - } - - // do not allow cards with attachments to stack with other cards - if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(xCoord, yCoord)) { - continue; - } - - cardsToMove.insert(MoveCardStruct{card, position, _card}); - } - // In case all moves were filtered out, abort. - if (cardsToMove.empty()) { - return Response::RespContextError; - } - - int xIndex = -1; - bool revealTopStart = false; - bool revealTopTarget = false; - - bool isFromBottom = shouldBeFromTheBottom(startzone, cardsToMove); - - if (isFromBottom) { - std::ranges::reverse_view reversedCardsToMove{cardsToMove}; - for (auto card : reversedCardsToMove) { - processMoveCard(ges, startzone, targetzone, card, xCoord, yCoord, xIndex, revealTopStart, revealTopTarget, - isReversed, undoingDraw); - } - } else { - for (auto card : cardsToMove) { - processMoveCard(ges, startzone, targetzone, card, xCoord, yCoord, xIndex, revealTopStart, revealTopTarget, - isReversed, undoingDraw); - } - } - - if (revealTopStart) { - revealTopCardIfNeeded(startzone, ges); - } - if (targetzone != startzone && revealTopTarget) { - revealTopCardIfNeeded(targetzone, ges); - } - if (undoingDraw) { - ges.setGameEventContext(Context_UndoDraw()); - } else { - ges.setGameEventContext(Context_MoveCard()); - } - - if (startzone->hasCoords() && fixFreeSpaces) { - startzone->fixFreeSpaces(ges); - } - - return Response::RespOk; -} - -void Server_AbstractPlayer::processMoveCard(GameEventStorage &ges, - Server_CardZone *startzone, - Server_CardZone *targetzone, - MoveCardStruct cardStruct, - int xCoord, - int yCoord, - int &xIndex, - bool &revealTopStart, - bool &revealTopTarget, - bool isReversed, - bool undoingDraw) -{ - Server_Card *card = cardStruct.card; - int originalPosition = cardStruct.position; - - bool sourceBeingLookedAt; - int position = startzone->removeCard(card, sourceBeingLookedAt); - - // Attachment relationships can be retained when moving a card onto the opponent's table - if (startzone->getName() != targetzone->getName()) { - // Delete all attachment relationships - if (card->getParentCard()) { - card->setParentCard(nullptr); - } - - // Make a copy of the list because the original one gets modified during the loop - QList attachedCards = card->getAttachedCards(); - for (auto &attachedCard : attachedCards) { - attachedCard->getZone()->getPlayer()->unattachCard(ges, attachedCard); - } - } - - if (startzone != targetzone) { - // Delete all arrows from and to the card - for (auto *player : game->getPlayers().values()) { - QList arrowsToDelete; - for (Server_Arrow *arrow : player->getArrows()) { - if ((arrow->getStartCard() == card) || (arrow->getTargetItem() == card)) - arrowsToDelete.append(arrow->getId()); - } - for (int j : arrowsToDelete) { - player->deleteArrow(j); - } - } - } - - if (shouldDestroyOnMove(card, startzone, targetzone)) { - Event_DestroyCard event; - event.set_zone_name(startzone->getName().toStdString()); - event.set_card_id(static_cast(card->getId())); - ges.enqueueGameEvent(event, playerId); - - if (Server_Card *stashedCard = card->takeStashedCard()) { - stashedCard->setId(newCardId()); - ges.enqueueGameEvent(makeCreateTokenEvent(startzone, stashedCard, card->getX(), card->getY()), playerId); - card->deleteLater(); - card = stashedCard; - } else { - card->deleteLater(); - card = nullptr; - } - } - - if (card) { - ++xIndex; - int newX = isReversed ? targetzone->getCards().size() - xCoord + xIndex : xCoord + xIndex; - - bool faceDown = shouldBeFaceDown(cardStruct, startzone, targetzone); - - if (targetzone->hasCoords()) { - newX = targetzone->getFreeGridColumn(newX, yCoord, card->getName(), faceDown); - } else { - card->resetState(targetzone->getName() == ZoneNames::STACK); - } - - targetzone->insertCard(card, newX, yCoord); - int targetLookedCards = targetzone->getCardsBeingLookedAt(); - bool sourceKnownToPlayer = isReversed || (sourceBeingLookedAt && !card->getFaceDown()); - if (targetzone->getType() == ServerInfo_Zone::HiddenZone && targetLookedCards >= newX) { - if (sourceKnownToPlayer) { - targetLookedCards += 1; - } else { - targetLookedCards = newX; - } - targetzone->setCardsBeingLookedAt(targetLookedCards); - } - - bool targetHiddenToOthers = faceDown || (targetzone->getType() != ServerInfo_Zone::PublicZone); - bool sourceHiddenToOthers = card->getFaceDown() || (startzone->getType() != ServerInfo_Zone::PublicZone); - - int oldCardId = card->getId(); - if ((faceDown && (startzone != targetzone)) || (targetzone->getPlayer() != startzone->getPlayer())) { - card->setId(targetzone->getPlayer()->newCardId()); - } - card->setFaceDown(faceDown); - - Event_MoveCard eventOthers; - eventOthers.set_start_player_id(startzone->getPlayer()->getPlayerId()); - eventOthers.set_start_zone(startzone->getName().toStdString()); - eventOthers.set_target_player_id(targetzone->getPlayer()->getPlayerId()); - if (startzone != targetzone) { - eventOthers.set_target_zone(targetzone->getName().toStdString()); - } - eventOthers.set_y(yCoord); - eventOthers.set_face_down(faceDown); - - Event_MoveCard eventPrivate(eventOthers); - if (sourceBeingLookedAt || targetzone->getType() != ServerInfo_Zone::HiddenZone || - startzone->getType() != ServerInfo_Zone::HiddenZone) { - eventPrivate.set_card_id(oldCardId); - eventPrivate.set_new_card_id(card->getId()); - } else { - eventPrivate.set_card_id(-1); - eventPrivate.set_new_card_id(-1); - } - if (sourceKnownToPlayer || !(faceDown || targetzone->getType() == ServerInfo_Zone::HiddenZone)) { - QString privateCardName = card->getName(); - eventPrivate.set_card_name(privateCardName.toStdString()); - eventPrivate.set_new_card_provider_id(card->getProviderId().toStdString()); - } - if (startzone->getType() == ServerInfo_Zone::HiddenZone) { - eventPrivate.set_position(position); - } else { - eventPrivate.set_position(-1); - } - - eventPrivate.set_x(newX); - - if ( - // cards from public zones have their id known, their previous position is already known, the event does - // not accomodate for previous locations in zones with coordinates (which are always public) - (startzone->getType() != ServerInfo_Zone::PublicZone) && - // other players are not allowed to be able to track which card is which in private zones like the hand - (startzone->getType() != ServerInfo_Zone::PrivateZone)) { - eventOthers.set_position(position); - } - if ( - // other players are not allowed to be able to track which card is which in private zones like the hand - (targetzone->getType() != ServerInfo_Zone::PrivateZone)) { - eventOthers.set_x(newX); - } - - if ((startzone->getType() == ServerInfo_Zone::PublicZone) || - (targetzone->getType() == ServerInfo_Zone::PublicZone)) { - eventOthers.set_card_id(oldCardId); - if (!(sourceHiddenToOthers && targetHiddenToOthers)) { - QString publicCardName = card->getName(); - eventOthers.set_card_name(publicCardName.toStdString()); - eventOthers.set_new_card_provider_id(card->getProviderId().toStdString()); - } - eventOthers.set_new_card_id(card->getId()); - } - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - - if (originalPosition == 0) { - revealTopStart = true; - } - if (newX == 0) { - revealTopTarget = true; - } - - // handle side effects for this card - onCardBeingMoved(ges, cardStruct, startzone, targetzone, undoingDraw); - } -} - -void Server_AbstractPlayer::onCardBeingMoved(GameEventStorage &ges, - const MoveCardStruct &cardStruct, - Server_CardZone *startzone, - Server_CardZone *targetzone, - bool /*undoingDraw*/) -{ - Server_Card *card = cardStruct.card; - const CardToMove *thisCardProperties = cardStruct.cardToMove; - - // set card to be tapped - if (thisCardProperties->tapped()) { - setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(), AttrTapped, - "1"); - } - - // set card pt - QString ptString = QString::fromStdString(thisCardProperties->pt()); - if (!ptString.isEmpty()) { - setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(), AttrPT, - ptString); - } - - // If card is transferring to a different player, leave an annotation of who actually "owns" the card - const auto &priorAnnotation = card->getAnnotation(); - if (startzone->getPlayer() != targetzone->getPlayer() && !priorAnnotation.contains("Owner:")) { - const auto &ownerAnnotation = "Owner: " + QString::fromStdString(startzone->getPlayer()->getUserInfo()->name()); - const auto &newAnnotation = - priorAnnotation.isEmpty() ? ownerAnnotation : ownerAnnotation + "\n\n" + priorAnnotation; - setCardAttrHelper(ges, targetzone->getPlayer()->getPlayerId(), targetzone->getName(), card->getId(), - AttrAnnotation, newAnnotation, card); - } -} - -void Server_AbstractPlayer::revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges) -{ - if (zone->getCards().isEmpty()) { - return; - } - if (zone->getAlwaysRevealTopCard()) { - Event_RevealCards revealEvent; - revealEvent.set_zone_name(zone->getName().toStdString()); - revealEvent.add_card_id(0); - zone->getCards().first()->getInfo(revealEvent.add_cards()); - - ges.enqueueGameEvent(revealEvent, playerId); - return; - } - if (zone->getAlwaysLookAtTopCard()) { - Event_DumpZone dumpEvent; - dumpEvent.set_zone_owner_id(playerId); - dumpEvent.set_zone_name(zone->getName().toStdString()); - dumpEvent.set_number_cards(1); - ges.enqueueGameEvent(dumpEvent, playerId, GameEventStorageItem::SendToOthers); - - Event_RevealCards revealEvent; - revealEvent.set_zone_name(zone->getName().toStdString()); - revealEvent.set_number_of_cards(1); - revealEvent.add_card_id(0); - zone->getCards().first()->getInfo(revealEvent.add_cards()); - ges.enqueueGameEvent(revealEvent, playerId, GameEventStorageItem::SendToPrivate, playerId); - } -} - -void Server_AbstractPlayer::unattachCard(GameEventStorage &ges, Server_Card *card) -{ - Server_CardZone *zone = card->getZone(); - Server_Card *parentCard = card->getParentCard(); - card->setParentCard(nullptr); - - ges.enqueueGameEvent(makeAttachCardEvent(card), playerId); - - auto *cardToMove = new CardToMove; - cardToMove->set_card_id(card->getId()); - moveCard(ges, zone, QList() << cardToMove, zone, -1, card->getY(), card->getFaceDown()); - delete cardToMove; - - if (parentCard->getZone()) { - parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY()); - } -} - -Response::ResponseCode Server_AbstractPlayer::setCardAttrHelper(GameEventStorage &ges, - int targetPlayerId, - const QString &zoneName, - int cardId, - CardAttribute attribute, - const QString &attrValue, - Server_Card *unzonedCard) -{ - Server_CardZone *zone = getZones().value(zoneName); - if (!zone) { - return Response::RespNameNotFound; - } - if (!zone->hasCoords()) { - return Response::RespContextError; - } - - QString result; - if (cardId == -1) { - QListIterator CardIterator(zone->getCards()); - while (CardIterator.hasNext()) { - result = CardIterator.next()->setAttribute(attribute, attrValue, true); - if (result.isNull()) { - return Response::RespInvalidCommand; - } - } - } else { - Server_Card *card = unzonedCard == nullptr ? zone->getCard(cardId) : unzonedCard; - if (!card) { - return Response::RespNameNotFound; - } - result = card->setAttribute(attribute, attrValue, false); - if (result.isNull()) { - return Response::RespInvalidCommand; - } - } - - Event_SetCardAttr event; - event.set_zone_name(zone->getName().toStdString()); - if (cardId != -1) { - event.set_card_id(cardId); - } - event.set_attribute(attribute); - event.set_attr_value(result.toStdString()); - ges.enqueueGameEvent(event, targetPlayerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdConcede(const Command_Concede & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - setConceded(true); - game->removeArrowsRelatedToPlayer(ges, this); - game->unattachCards(ges, this); - game->returnCardsFromPlayer(ges, this); - - clearZones(); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_conceded(true); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_Concede()); - - game->stopGameIfFinished(); - if (game->getGameStarted() && (game->getActivePlayer() == playerId)) { - game->nextTurn(); - } - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractPlayer::cmdUnconcede(const Command_Unconcede & /*cmd*/, - ResponseContainer & /*rc*/, - GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (!conceded) { - return Response::RespContextError; - } - - setConceded(false); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_conceded(false); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_Unconcede()); - - setupZones(); - - game->sendGameStateToPlayers(); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!deck || game->getGameStarted()) { - return Response::RespContextError; - } - - if (readyStart == cmd.ready() && !cmd.force_start()) { - return Response::RespContextError; - } - - setReadyStart(cmd.ready()); - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ready_start(cmd.ready()); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_ReadyStart()); - - if (cmd.force_start()) { - if (game->getHostId() != playerId) { - return Response::RespFunctionNotAllowed; - } - game->startGameIfReady(true); - } else if (cmd.ready()) { - game->startGameIfReady(false); - } - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdRollDie(const Command_RollDie &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) const -{ - if (conceded) { - return Response::RespContextError; - } - - const auto validatedSides = static_cast(std::min(std::max(cmd.sides(), MINIMUM_DIE_SIDES), MAXIMUM_DIE_SIDES)); - const auto validatedDiceToRoll = - static_cast(std::min(std::max(cmd.count(), MINIMUM_DICE_TO_ROLL), MAXIMUM_DICE_TO_ROLL)); - - Event_RollDie event; - event.set_sides(validatedSides); - for (auto i = 0; i < validatedDiceToRoll; ++i) { - const auto roll = rng->rand(1, validatedSides); - if (i == 0) { - // Backwards compatibility - event.set_value(roll); - } - event.add_values(roll); - } - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_AbstractPlayer *startPlayer = game->getPlayer(cmd.has_start_player_id() ? cmd.start_player_id() : playerId); - if (!startPlayer) { - return Response::RespNameNotFound; - } - Server_CardZone *startZone = startPlayer->getZones().value(nameFromStdString(cmd.start_zone())); - if (!startZone) { - return Response::RespNameNotFound; - } - - if ((startPlayer != this) && (!startZone->getPlayersWithWritePermission().contains(playerId)) && !judge) { - return Response::RespContextError; - } - - Server_AbstractPlayer *targetPlayer = game->getPlayer(cmd.target_player_id()); - if (!targetPlayer) { - return Response::RespNameNotFound; - } - Server_CardZone *targetZone = targetPlayer->getZones().value(nameFromStdString(cmd.target_zone())); - if (!targetZone) { - return Response::RespNameNotFound; - } - - if ((startPlayer != this) && (targetPlayer != this) && !judge) { - return Response::RespContextError; - } - - QList cardsToMove; - for (int i = 0; i < cmd.cards_to_move().card_size(); ++i) { - cardsToMove.append(&cmd.cards_to_move().card(i)); - } - - return moveCard(ges, startZone, cardsToMove, targetZone, cmd.x(), cmd.y(), true, false, cmd.is_reversed()); -} - -Response::ResponseCode -Server_AbstractPlayer::cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone())); - if (!zone) { - return Response::RespNameNotFound; - } - if (!zone->hasCoords()) { - return Response::RespContextError; - } - - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) { - return Response::RespNameNotFound; - } - - const bool faceDown = cmd.face_down(); - if (faceDown == card->getFaceDown()) { - return Response::RespContextError; - } - - card->setFaceDown(faceDown); - - Event_FlipCard event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - if (!faceDown) { - event.set_card_name(card->getName().toStdString()); - event.set_card_provider_id(card->getProviderId().toStdString()); - } - event.set_face_down(faceDown); - ges.enqueueGameEvent(event, playerId); - - QString ptString = nameFromStdString(cmd.pt()); - if (!ptString.isEmpty() && !faceDown) { - setCardAttrHelper(ges, playerId, zone->getName(), card->getId(), AttrPT, ptString); - } - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *startzone = zones.value(nameFromStdString(cmd.start_zone())); - if (!startzone) { - return Response::RespNameNotFound; - } - - Server_Card *card = startzone->getCard(cmd.card_id()); - if (!card) { - return Response::RespNameNotFound; - } - - Server_AbstractPlayer *targetPlayer = nullptr; - Server_CardZone *targetzone = nullptr; - Server_Card *targetCard = nullptr; - - if (cmd.has_target_player_id()) { - targetPlayer = game->getPlayer(cmd.target_player_id()); - if (!targetPlayer) { - return Response::RespNameNotFound; - } - } else if (!card->getParentCard()) { - return Response::RespContextError; - } - if (targetPlayer) { - targetzone = targetPlayer->getZones().value(nameFromStdString(cmd.target_zone())); - } - if (targetzone) { - // This is currently enough to make sure cards don't get attached to a card that is not on the table. - // Possibly a flag will have to be introduced for this sometime. - if (!targetzone->hasCoords()) { - return Response::RespContextError; - } - if (cmd.has_target_card_id()) { - targetCard = targetzone->getCard(cmd.target_card_id()); - } - if (targetCard) { - if (targetCard->getParentCard()) { - return Response::RespContextError; - } - } else { - return Response::RespNameNotFound; - } - } - - // prevent attaching from non-table zones - // (attaching from non-table zones is handled client-side by moving the card to table zone first) - if (!startzone->hasCoords()) { - return Response::RespContextError; - } - - for (auto *player : game->getPlayers()) { - QList _arrows = player->getArrows().values(); - QList toDelete; - for (auto a : _arrows) { - auto *tCard = qobject_cast(a->getTargetItem()); - if ((tCard == card) || (a->getStartCard() == card)) { - toDelete.append(a); - } - } - for (auto &i : toDelete) { - Event_DeleteArrow event; - event.set_arrow_id(i->getId()); - ges.enqueueGameEvent(event, player->getPlayerId()); - player->deleteArrow(i->getId()); - } - } - - if (targetCard) { - // Unattach all cards attached to the card being attached. - // Make a copy of the list because its contents change during the loop otherwise. - QList attachedList = card->getAttachedCards(); - for (const auto &i : attachedList) { - i->getZone()->getPlayer()->unattachCard(ges, i); - } - - card->setParentCard(targetCard); - const int oldX = card->getX(); - card->setCoords(-1, card->getY()); - startzone->updateCardCoordinates(card, oldX, card->getY()); - - if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) { - auto *cardToMove = new CardToMove; - cardToMove->set_card_id(targetCard->getId()); - targetPlayer->moveCard(ges, targetzone, QList() << cardToMove, targetzone, - targetzone->getFreeGridColumn(-2, targetCard->getY(), targetCard->getName(), false), - targetCard->getY(), targetCard->getFaceDown()); - delete cardToMove; - } - - ges.enqueueGameEvent(makeAttachCardEvent(card, targetCard), playerId); - - startzone->fixFreeSpaces(ges); - } else { - unattachCard(ges, card); - } - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone())); - if (!zone) { - return Response::RespNameNotFound; - } - - int xCoord = cmd.x(); - int yCoord = cmd.y(); - - Server_Card *targetCard = nullptr; - if (cmd.has_target_card_id()) { - Server_CardZone *targetZone = zones.value(nameFromStdString(cmd.target_zone())); - if (targetZone) { - targetCard = targetZone->getCard(cmd.target_card_id()); - if (targetCard && cmd.target_mode() == Command_CreateToken::TRANSFORM_INTO) { - if (targetCard->getParentCard()) { - ges.enqueueGameEvent(makeAttachCardEvent(targetCard), playerId); - } - - for (Server_Card *attachedCard : targetCard->getAttachedCards()) { - ges.enqueueGameEvent(makeAttachCardEvent(attachedCard), - attachedCard->getZone()->getPlayer()->getPlayerId()); - } - - if (zone->hasCoords() && zone == targetZone) { - xCoord = targetCard->getX(); - yCoord = targetCard->getY(); - } - - targetZone->removeCard(targetCard); - - Event_DestroyCard event; - event.set_zone_name(targetZone->getName().toStdString()); - event.set_card_id(static_cast<::google::protobuf::uint32>(cmd.target_card_id())); - ges.enqueueGameEvent(event, playerId); - } - } - } - - const QString cardName = nameFromStdString(cmd.card_name()); - const QString cardProviderId = nameFromStdString(cmd.card_provider_id()); - if (zone->hasCoords()) { - bool dontStackSameName = cmd.face_down(); - xCoord = zone->getFreeGridColumn(xCoord, yCoord, cardName, dontStackSameName); - } - if (xCoord < 0) { - xCoord = 0; - } - if (yCoord < 0) { - yCoord = 0; - } - - auto *card = new Server_Card({cardName, cardProviderId}, newCardId(), xCoord, yCoord); - card->moveToThread(thread()); - // Client should already prevent face-down tokens from having attributes; this just an extra server-side check - if (!cmd.face_down()) { - card->setColor(nameFromStdString(cmd.color())); - card->setPT(nameFromStdString(cmd.pt())); - } - card->setAnnotation(nameFromStdString(cmd.annotation())); - card->setDestroyOnZoneChange(cmd.destroy_on_zone_change()); - card->setFaceDown(cmd.face_down()); - - zone->insertCard(card, xCoord, yCoord); - sendCreateTokenEvents(zone, card, xCoord, yCoord, ges); - - // check if the token is a replacement for an existing card - if (!targetCard) { - return Response::RespOk; - } - - switch (cmd.target_mode()) { - case Command_CreateToken::ATTACH_TO: { - Command_AttachCard cmd2; - cmd2.set_start_zone(cmd.target_zone()); - cmd2.set_card_id(cmd.target_card_id()); - - cmd2.set_target_player_id(zone->getPlayer()->getPlayerId()); - cmd2.set_target_zone(cmd.zone()); - cmd2.set_target_card_id(card->getId()); - - return cmdAttachCard(cmd2, rc, ges); - } - - case Command_CreateToken::TRANSFORM_INTO: { - // Copy attributes that are not present in the CreateToken event - Event_SetCardAttr event; - event.set_zone_name(card->getZone()->getName().toStdString()); - event.set_card_id(card->getId()); - - if (card->getTapped() != targetCard->getTapped()) { - card->setAttribute(AttrTapped, QVariant(targetCard->getTapped()).toString(), &event); - ges.enqueueGameEvent(event, playerId); - } - - if (card->getAttacking() != targetCard->getAttacking()) { - card->setAttribute(AttrAttacking, QVariant(targetCard->getAttacking()).toString(), &event); - ges.enqueueGameEvent(event, playerId); - } - - if (card->getFaceDown() != targetCard->getFaceDown()) { - card->setAttribute(AttrFaceDown, QVariant(targetCard->getFaceDown()).toString(), &event); - ges.enqueueGameEvent(event, playerId); - } - - if (card->getDoesntUntap() != targetCard->getDoesntUntap()) { - card->setAttribute(AttrDoesntUntap, QVariant(targetCard->getDoesntUntap()).toString(), &event); - ges.enqueueGameEvent(event, playerId); - } - - // Copy counters - QMapIterator i(targetCard->getCounters()); - while (i.hasNext()) { - i.next(); - - Event_SetCardCounter _event; - _event.set_zone_name(card->getZone()->getName().toStdString()); - _event.set_card_id(card->getId()); - - card->setCounter(i.key(), i.value(), &_event); - ges.enqueueGameEvent(_event, playerId); - } - - // Copy parent card - if (Server_Card *parentCard = targetCard->getParentCard()) { - targetCard->setParentCard(nullptr); - card->setParentCard(parentCard); - - ges.enqueueGameEvent(makeAttachCardEvent(card, parentCard), playerId); - } - - // Copy attachments - while (!targetCard->getAttachedCards().isEmpty()) { - Server_Card *attachedCard = targetCard->getAttachedCards().last(); - attachedCard->setParentCard(card); - - ges.enqueueGameEvent(makeAttachCardEvent(attachedCard, card), - attachedCard->getZone()->getPlayer()->getPlayerId()); - } - - // Copy Arrows - for (auto *player : game->getPlayers().values()) { - QList changedArrowIds; - for (Server_Arrow *arrow : player->getArrows()) { - bool sendGameEvent = false; - const auto *startCard = arrow->getStartCard(); - if (startCard == targetCard) { - sendGameEvent = true; - arrow->setStartCard(card); - startCard = card; - } - const auto *targetItem = arrow->getTargetItem(); - if (targetItem == targetCard) { - sendGameEvent = true; - arrow->setTargetItem(card); - targetItem = card; - } - if (sendGameEvent) { - Event_CreateArrow _event; - ServerInfo_Arrow *arrowInfo = _event.mutable_arrow_info(); - changedArrowIds.append(arrow->getId()); - int id = player->newArrowId(); - arrow->setId(id); - arrowInfo->set_id(id); - arrowInfo->set_start_player_id(player->getPlayerId()); - arrowInfo->set_start_zone(startCard->getZone()->getName().toStdString()); - arrowInfo->set_start_card_id(startCard->getId()); - const auto *arrowTargetPlayer = qobject_cast(targetItem); - if (arrowTargetPlayer != nullptr) { - arrowInfo->set_target_player_id(arrowTargetPlayer->getPlayerId()); - } else { - const auto *arrowTargetCard = qobject_cast(targetItem); - arrowInfo->set_target_player_id(arrowTargetCard->getZone()->getPlayer()->getPlayerId()); - arrowInfo->set_target_zone(arrowTargetCard->getZone()->getName().toStdString()); - arrowInfo->set_target_card_id(arrowTargetCard->getId()); - } - arrowInfo->mutable_arrow_color()->CopyFrom(arrow->getColor()); - ges.enqueueGameEvent(_event, player->getPlayerId()); - } - } - for (int id : changedArrowIds) { - player->updateArrowId(id); - } - } - - targetCard->resetState(); - card->setStashedCard(targetCard); - break; - } - } - - return Response::RespOk; -} - -/** - * Creates and sends the events required to properly communicate the given token creation. - * Primarily written to handle creating face-down tokens. - */ -void Server_AbstractPlayer::sendCreateTokenEvents(Server_CardZone *zone, - Server_Card *card, - int xCoord, - int yCoord, - GameEventStorage &ges) -{ - // Token is not face-down; things are easy - if (!card->getFaceDown()) { - ges.enqueueGameEvent(makeCreateTokenEvent(zone, card, xCoord, yCoord), playerId); - return; - } - - // Token is face-down. We have to send different info to each player - auto eventOthers = makeCreateTokenEvent(zone, card, xCoord, yCoord, false); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - - auto eventPrivate = makeCreateTokenEvent(zone, card, xCoord, yCoord, true); - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); - - // Event_CreateToken didn't use to have face_down field; send attribute event afterward for backwards compatibility - Event_SetCardAttr event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_attribute(AttrFaceDown); - event.set_attr_value("1"); - ges.enqueueGameEvent(event, playerId); -} - -Response::ResponseCode -Server_AbstractPlayer::cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_AbstractPlayer *startPlayer = game->getPlayer(cmd.start_player_id()); - Server_AbstractPlayer *targetPlayer = game->getPlayer(cmd.target_player_id()); - if (!startPlayer || !targetPlayer) { - return Response::RespNameNotFound; - } - QString startZoneName = nameFromStdString(cmd.start_zone()); - Server_CardZone *startZone = startPlayer->getZones().value(startZoneName); - bool playerTarget = !cmd.has_target_zone(); - Server_CardZone *targetZone = nullptr; - if (!playerTarget) { - targetZone = targetPlayer->getZones().value(nameFromStdString(cmd.target_zone())); - } - if (!startZone || (!targetZone && !playerTarget)) { - return Response::RespNameNotFound; - } - if (startZone->getType() != ServerInfo_Zone::PublicZone) { - return Response::RespContextError; - } - Server_Card *startCard = startZone->getCard(cmd.start_card_id()); - if (!startCard) { - return Response::RespNameNotFound; - } - Server_Card *targetCard = nullptr; - if (!playerTarget) { - if (targetZone->getType() != ServerInfo_Zone::PublicZone) { - return Response::RespContextError; - } - targetCard = targetZone->getCard(cmd.target_card_id()); - } - - Server_ArrowTarget *targetItem; - if (playerTarget) { - targetItem = targetPlayer; - } else { - targetItem = targetCard; - } - if (!targetItem) { - return Response::RespNameNotFound; - } - - for (Server_Arrow *temp : arrows) { - if ((temp->getStartCard() == startCard) && (temp->getTargetItem() == targetItem)) { - return Response::RespContextError; - } - } - - int currentPhase = game->getActivePhase(); - int deletionPhase = cmd.has_delete_in_phase() ? cmd.delete_in_phase() : currentPhase; - auto arrow = new Server_Arrow(newArrowId(), startCard, targetItem, cmd.arrow_color(), currentPhase, deletionPhase); - addArrow(arrow); - - Event_CreateArrow event; - ServerInfo_Arrow *arrowInfo = event.mutable_arrow_info(); - arrowInfo->set_id(arrow->getId()); - arrowInfo->set_start_player_id(startPlayer->getPlayerId()); - arrowInfo->set_start_zone(startZoneName.toStdString()); - arrowInfo->set_start_card_id(startCard->getId()); - arrowInfo->set_target_player_id(targetPlayer->getPlayerId()); - if (!playerTarget) { - arrowInfo->set_target_zone(cmd.target_zone()); - arrowInfo->set_target_card_id(cmd.target_card_id()); - } - arrowInfo->mutable_arrow_color()->CopyFrom(cmd.arrow_color()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - if (!deleteArrow(cmd.arrow_id())) { - return Response::RespNameNotFound; - } - - Event_DeleteArrow event; - event.set_arrow_id(cmd.arrow_id()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - return setCardAttrHelper(ges, playerId, nameFromStdString(cmd.zone()), cmd.card_id(), cmd.attribute(), - nameFromStdString(cmd.attr_value())); -} - -Response::ResponseCode Server_AbstractPlayer::cmdSetCardCounter(const Command_SetCardCounter &cmd, - ResponseContainer & /*rc*/, - GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone())); - if (!zone) { - return Response::RespNameNotFound; - } - if (!zone->hasCoords()) { - return Response::RespContextError; - } - - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) { - return Response::RespNameNotFound; - } - - Event_SetCardCounter event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - card->setCounter(cmd.counter_id(), cmd.counter_value(), &event); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractPlayer::cmdIncCardCounter(const Command_IncCardCounter &cmd, - ResponseContainer & /*rc*/, - GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone())); - if (!zone) { - return Response::RespNameNotFound; - } - if (!zone->hasCoords()) { - return Response::RespContextError; - } - - Server_Card *card = zone->getCard(cmd.card_id()); - if (!card) { - return Response::RespNameNotFound; - } - - int newValue = card->getCounter(cmd.counter_id()) + cmd.counter_delta(); - card->setCounter(cmd.counter_id(), newValue); - - Event_SetCardCounter event; - event.set_zone_name(zone->getName().toStdString()); - event.set_card_id(card->getId()); - event.set_counter_id(cmd.counter_id()); - event.set_counter_value(newValue); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - Server_AbstractPlayer *otherPlayer = game->getPlayer(cmd.player_id()); - if (!otherPlayer) { - return Response::RespNameNotFound; - } - Server_CardZone *zone = otherPlayer->getZones().value(nameFromStdString(cmd.zone_name())); - if (!zone) { - return Response::RespNameNotFound; - } - if (!((zone->getType() == ServerInfo_Zone::PublicZone) || (this == otherPlayer))) { - return Response::RespContextError; - } - - int numberCards = cmd.number_cards(); - const QList &cards = zone->getCards(); - - auto *re = new Response_DumpZone; - ServerInfo_Zone *zoneInfo = re->mutable_zone_info(); - zoneInfo->set_name(zone->getName().toStdString()); - zoneInfo->set_type(zone->getType()); - zoneInfo->set_with_coords(zone->hasCoords()); - zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards); - - for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) { - const auto &findId = cmd.is_reversed() ? cards.size() - numberCards + i : i; - Server_Card *card = cards[findId]; - QString displayedName = card->getFaceDown() ? QString() : card->getName(); - ServerInfo_Card *cardInfo = zoneInfo->add_card_list(); - cardInfo->set_provider_id(card->getProviderId().toStdString()); - cardInfo->set_name(displayedName.toStdString()); - if (zone->getType() == ServerInfo_Zone::HiddenZone) { - cardInfo->set_id(findId); - } else { - cardInfo->set_id(card->getId()); - cardInfo->set_x(card->getX()); - cardInfo->set_y(card->getY()); - cardInfo->set_face_down(card->getFaceDown()); - cardInfo->set_tapped(card->getTapped()); - cardInfo->set_attacking(card->getAttacking()); - cardInfo->set_color(card->getColor().toStdString()); - cardInfo->set_pt(card->getPT().toStdString()); - cardInfo->set_annotation(card->getAnnotation().toStdString()); - cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - cardInfo->set_doesnt_untap(card->getDoesntUntap()); - - QMapIterator cardCounterIterator(card->getCounters()); - while (cardCounterIterator.hasNext()) { - cardCounterIterator.next(); - ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); - counterInfo->set_id(cardCounterIterator.key()); - counterInfo->set_value(cardCounterIterator.value()); - } - - if (card->getParentCard()) { - cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); - cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); - cardInfo->set_attach_card_id(card->getParentCard()->getId()); - } - } - } - if (zone->getType() == ServerInfo_Zone::HiddenZone) { - zone->setCardsBeingLookedAt(numberCards); - - Event_DumpZone event; - event.set_zone_owner_id(otherPlayer->getPlayerId()); - event.set_zone_name(zone->getName().toStdString()); - event.set_number_cards(numberCards); - event.set_is_reversed(cmd.is_reversed()); - ges.enqueueGameEvent(event, playerId); - } - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode -Server_AbstractPlayer::cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - if (cmd.has_player_id()) { - Server_AbstractPlayer *otherPlayer = game->getPlayer(cmd.player_id()); - if (!otherPlayer) - return Response::RespNameNotFound; - } - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone_name())); - if (!zone) { - return Response::RespNameNotFound; - } - - QList cardsToReveal; - if (cmd.top_cards() != -1) { - for (int i = 0; i < cmd.top_cards(); i++) { - Server_Card *card = zone->getCard(i); - if (!card) { - return Response::RespNameNotFound; - } - cardsToReveal.append(card); - } - } else if (cmd.card_id_size() == 0) { - cardsToReveal = zone->getCards(); - } else if (cmd.card_id_size() == 1 && cmd.card_id(0) == -2) { - // If there is a single card_id with value -2 (ie - // Player::RANDOM_CARD_FROM_ZONE), pick a random card. - // - // This is to be compatible with clients supporting a single card_id - // value, which send value -2 to request a random card. - if (zone->getCards().isEmpty()) { - return Response::RespContextError; - } - - cardsToReveal.append(zone->getCards().at(rng->rand(0, zone->getCards().size() - 1))); - } else { - for (auto cardId : cmd.card_id()) { - Server_Card *card = zone->getCard(cardId); - if (!card) { - return Response::RespNameNotFound; - } - cardsToReveal.append(card); - } - } - - Event_RevealCards eventOthers; - eventOthers.set_grant_write_access(cmd.grant_write_access()); - eventOthers.set_zone_name(zone->getName().toStdString()); - eventOthers.set_number_of_cards(cardsToReveal.size()); - for (auto cardId : cmd.card_id()) { - eventOthers.add_card_id(cardId); - } - if (cmd.has_player_id()) { - eventOthers.set_other_player_id(cmd.player_id()); - } - - Event_RevealCards eventPrivate(eventOthers); - - for (auto card : cardsToReveal) { - ServerInfo_Card *cardInfo = eventPrivate.add_cards(); - - cardInfo->set_id(card->getId()); - cardInfo->set_provider_id(card->getProviderId().toStdString()); - cardInfo->set_name(card->getName().toStdString()); - cardInfo->set_x(card->getX()); - cardInfo->set_y(card->getY()); - cardInfo->set_face_down(card->getFaceDown()); - cardInfo->set_tapped(card->getTapped()); - cardInfo->set_attacking(card->getAttacking()); - cardInfo->set_color(card->getColor().toStdString()); - cardInfo->set_pt(card->getPT().toStdString()); - cardInfo->set_annotation(card->getAnnotation().toStdString()); - cardInfo->set_destroy_on_zone_change(card->getDestroyOnZoneChange()); - cardInfo->set_doesnt_untap(card->getDoesntUntap()); - - QMapIterator cardCounterIterator(card->getCounters()); - while (cardCounterIterator.hasNext()) { - cardCounterIterator.next(); - ServerInfo_CardCounter *counterInfo = cardInfo->add_counter_list(); - counterInfo->set_id(cardCounterIterator.key()); - counterInfo->set_value(cardCounterIterator.value()); - } - - if (card->getParentCard()) { - cardInfo->set_attach_player_id(card->getParentCard()->getZone()->getPlayer()->getPlayerId()); - cardInfo->set_attach_zone(card->getParentCard()->getZone()->getName().toStdString()); - cardInfo->set_attach_card_id(card->getParentCard()->getId()); - } - } - - if (cmd.has_player_id()) { - if (cmd.grant_write_access()) { - zone->addWritePermission(cmd.player_id()); - } - - if (isJudge()) { - ges.setOverwriteOwnership(true); - } - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, cmd.player_id()); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - } else { - if (cmd.grant_write_access()) { - const QList &participantIds = game->getParticipants().keys(); - for (int anyParticipantId : participantIds) { - zone->addWritePermission(anyParticipantId); - } - } - - ges.enqueueGameEvent(eventPrivate, playerId); - } - - return Response::RespOk; -} - -Response::ResponseCode Server_AbstractPlayer::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, - ResponseContainer & /* rc */, - GameEventStorage &ges) -{ - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone_name())); - if (!zone) { - return Response::RespNameNotFound; - } - - Event_ChangeZoneProperties event; - event.set_zone_name(cmd.zone_name()); - - // Neither value set -> error. - if (!cmd.has_always_look_at_top_card() && !cmd.has_always_reveal_top_card()) { - return Response::RespContextError; - } - - // Neither value changed -> error. - bool alwaysRevealChanged = - cmd.has_always_reveal_top_card() && zone->getAlwaysRevealTopCard() != cmd.always_reveal_top_card(); - bool alwaysLookAtTopChanged = - cmd.has_always_look_at_top_card() && zone->getAlwaysLookAtTopCard() != cmd.always_look_at_top_card(); - if (!alwaysRevealChanged && !alwaysLookAtTopChanged) { - return Response::RespContextError; - } - - if (cmd.has_always_reveal_top_card()) { - zone->setAlwaysRevealTopCard(cmd.always_reveal_top_card()); - event.set_always_reveal_top_card(cmd.always_reveal_top_card()); - } - if (cmd.has_always_look_at_top_card()) { - zone->setAlwaysLookAtTopCard(cmd.always_look_at_top_card()); - event.set_always_look_at_top_card(cmd.always_look_at_top_card()); - } - ges.enqueueGameEvent(event, playerId); - return Response::RespOk; -} - -void Server_AbstractPlayer::getInfo(ServerInfo_Player *info, - Server_AbstractParticipant *recipient, - bool omniscient, - bool withUserInfo) -{ - getProperties(*info->mutable_properties(), withUserInfo); - if (recipient == this) { - if (deck) { - info->set_deck_list(deck->writeToString_Native().toStdString()); - } - } - - for (Server_Arrow *arrow : arrows) { - arrow->getInfo(info->add_arrow_list()); - } - - for (Server_CardZone *zone : zones) { - zone->getInfo(info->add_zone_list(), recipient, omniscient); - } -} - -void Server_AbstractPlayer::getPlayerProperties(ServerInfo_PlayerProperties &result) -{ - result.set_conceded(conceded); - result.set_sideboard_locked(sideboardLocked); - result.set_ready_start(readyStart); - if (deck) { - result.set_deck_hash(deck->getDeckHash().toStdString()); - } -} diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h deleted file mode 100644 index 9d9809298..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_abstract_player.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef ABSTRACT_PLAYER_H -#define ABSTRACT_PLAYER_H - -#include "../serverinfo_user_container.h" -#include "server_abstract_participant.h" - -#include -#include - -class CardToMove; -class DeckList; -class Server_Arrow; -class Server_Card; -class Server_CardZone; -class Server_Counter; -struct MoveCardStruct; - -class Server_AbstractPlayer : public Server_AbstractParticipant -{ - Q_OBJECT -private: - class MoveCardCompareFunctor; - QMap arrows; - - void sendCreateTokenEvents(Server_CardZone *zone, Server_Card *card, int xCoord, int yCoord, GameEventStorage &ges); - void getPlayerProperties(ServerInfo_PlayerProperties &result) override; - -protected: - bool conceded; - DeckList *deck; - bool sideboardLocked; - QMap zones; - bool readyStart; - int nextCardId; - - void revealTopCardIfNeeded(Server_CardZone *zone, GameEventStorage &ges); - -public: - Server_AbstractPlayer(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_handler); - ~Server_AbstractPlayer() override; - void prepareDestroy() override; - const DeckList *getDeckList() const - { - return deck; - } - bool getReadyStart() const - { - return readyStart; - } - void setReadyStart(bool _readyStart) - { - readyStart = _readyStart; - } - bool getConceded() const - { - return conceded; - } - void setConceded(bool _conceded) - { - conceded = _conceded; - } - - const QMap &getZones() const - { - return zones; - } - const QMap &getArrows() const - { - return arrows; - } - - int newCardId(); - int newArrowId() const; - - void addZone(Server_CardZone *zone); - void addArrow(Server_Arrow *arrow); - void updateArrowId(int id); - bool deleteArrow(int arrowId); - - virtual void setupZones(); - virtual void clearZones(); - - Response::ResponseCode moveCard(GameEventStorage &ges, - Server_CardZone *startzone, - const QList &_cards, - Server_CardZone *targetzone, - int xCoord, - int yCoord, - bool fixFreeSpaces = true, - bool undoingDraw = false, - bool isReversed = false); - - void processMoveCard(GameEventStorage &ges, - Server_CardZone *startzone, - Server_CardZone *targetzone, - MoveCardStruct cardStruct, - int xCoord, - int yCoord, - int &xIndex, - bool &revealTopStart, - bool &revealTopTarget, - bool isReversed, - bool undoingDraw); - - virtual void onCardBeingMoved(GameEventStorage &ges, - const MoveCardStruct &cardStruct, - Server_CardZone *startzone, - Server_CardZone *targetzone, - bool undoingDraw); - - void unattachCard(GameEventStorage &ges, Server_Card *card); - Response::ResponseCode setCardAttrHelper(GameEventStorage &ges, - int targetPlayerId, - const QString &zone, - int cardId, - CardAttribute attribute, - const QString &attrValue, - Server_Card *unzonedCard = nullptr); - - virtual Response::ResponseCode - cmdConcede(const Command_Concede &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdUnconcede(const Command_Unconcede &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdReadyStart(const Command_ReadyStart &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdRollDie(const Command_RollDie &cmd, ResponseContainer &rc, GameEventStorage &ges) const override; - virtual Response::ResponseCode - cmdMoveCard(const Command_MoveCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdFlipCard(const Command_FlipCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdAttachCard(const Command_AttachCard &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdCreateToken(const Command_CreateToken &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdCreateArrow(const Command_CreateArrow &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdDeleteArrow(const Command_DeleteArrow &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdSetCardAttr(const Command_SetCardAttr &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdSetCardCounter(const Command_SetCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdIncCardCounter(const Command_IncCardCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdDumpZone(const Command_DumpZone &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode - cmdRevealCards(const Command_RevealCards &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - virtual Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, - ResponseContainer &rc, - GameEventStorage &ges) override; - - virtual void getInfo(ServerInfo_Player *info, - Server_AbstractParticipant *playerWhosAsking, - bool omniscient, - bool withUserInfo) override; -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_arrow.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_arrow.h deleted file mode 100644 index 1f302358b..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_arrow.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef SERVER_ARROW_H -#define SERVER_ARROW_H - -#include - -class Server_Card; -class Server_ArrowTarget; -class ServerInfo_Arrow; - -class Server_Arrow -{ -private: - int id; - Server_Card *startCard; - Server_ArrowTarget *targetItem; - color arrowColor; - int phaseCreated, phaseDeleted; - -public: - Server_Arrow(int _id, - Server_Card *_startCard, - Server_ArrowTarget *_targetItem, - const color &_arrowColor, - int _phaseCreated, - int _phaseDeleted); - int getId() const - { - return id; - } - void setId(int _id) - { - id = _id; - } - Server_Card *getStartCard() const - { - return startCard; - } - void setStartCard(Server_Card *startCard_) - { - startCard = startCard_; - } - Server_ArrowTarget *getTargetItem() const - { - return targetItem; - } - void setTargetItem(Server_ArrowTarget *targetItem_) - { - targetItem = targetItem_; - } - const color &getColor() const - { - return arrowColor; - } - bool checkPhaseDeletion(int phase) const // returns true if the arrow should be deleted in this phase - { - return phase < phaseCreated || phase >= phaseDeleted; - } - - void getInfo(ServerInfo_Arrow *info); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.h deleted file mode 100644 index bc326bbc4..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_card.h +++ /dev/null @@ -1,227 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Max-Wilhelm Bruker * - * brukie@laptop * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef SERVER_CARD_H -#define SERVER_CARD_H - -#include "server_arrowtarget.h" - -#include -#include -#include -#include -#include - -class Server_CardZone; -class Event_SetCardCounter; -class Event_SetCardAttr; - -class Server_Card : public Server_ArrowTarget -{ - Q_OBJECT -private: - Server_CardZone *zone; - int id; - int coord_x, coord_y; - CardRef cardRef; - QMap counters; - bool tapped; - bool attacking; - bool facedown; - QString color; - QString ptString; - QString annotation; - bool destroyOnZoneChange; - bool doesntUntap; - - Server_Card *parentCard; - QList attachedCards; - Server_Card *stashedCard; - -public: - Server_Card(const CardRef &cardRef, int _id, int _coord_x, int _coord_y, Server_CardZone *_zone = nullptr); - ~Server_Card() override; - - Server_CardZone *getZone() const - { - return zone; - } - void setZone(Server_CardZone *_zone) - { - zone = _zone; - } - - int getId() const - { - return id; - } - CardRef getCardRef() const - { - return cardRef; - } - QString getProviderId() const - { - return cardRef.providerId; - } - int getX() const - { - return coord_x; - } - int getY() const - { - return coord_y; - } - QString getName() const - { - return cardRef.name; - } - const QMap &getCounters() const - { - return counters; - } - int getCounter(int counter_id) const - { - return counters.value(counter_id, 0); - } - bool getTapped() const - { - return tapped; - } - bool getAttacking() const - { - return attacking; - } - bool getFaceDown() const - { - return facedown; - } - QString getColor() const - { - return color; - } - QString getPT() const - { - return ptString; - } - QString getAnnotation() const - { - return annotation; - } - bool getDoesntUntap() const - { - return doesntUntap; - } - bool getDestroyOnZoneChange() const - { - return destroyOnZoneChange; - } - Server_Card *getParentCard() const - { - return parentCard; - } - const QList &getAttachedCards() const - { - return attachedCards; - } - - void setId(int _id) - { - id = _id; - } - void setCoords(int x, int y) - { - coord_x = x; - coord_y = y; - } - void setCardRef(const CardRef &_cardRef) - { - cardRef = _cardRef; - } - void setCounter(int _id, int value, Event_SetCardCounter *event = nullptr); - void setTapped(bool _tapped) - { - tapped = _tapped; - } - void setAttacking(bool _attacking) - { - attacking = _attacking; - } - void setFaceDown(bool _facedown) - { - facedown = _facedown; - } - void setColor(const QString &_color) - { - color = _color; - } - void setPT(const QString &_pt) - { - ptString = _pt; - } - void setAnnotation(const QString &_annotation) - { - annotation = _annotation; - } - void setDestroyOnZoneChange(bool _destroy) - { - destroyOnZoneChange = _destroy; - } - void setDoesntUntap(bool _doesntUntap) - { - doesntUntap = _doesntUntap; - } - void setParentCard(Server_Card *_parentCard); - void addAttachedCard(Server_Card *card) - { - attachedCards.append(card); - } - void removeAttachedCard(Server_Card *card) - { - attachedCards.removeOne(card); - } - void setStashedCard(Server_Card *card) - { - // setStashedCard should only be called on creation of a new card, so - // there should never be an already existing stashed card. - Q_ASSERT(!stashedCard); - - // Stashed cards can't themselves have stashed cards, and tokens can't - // be stashed. - if (card->stashedCard || card->getDestroyOnZoneChange()) { - stashedCard = card->takeStashedCard(); - card->deleteLater(); - } else { - stashedCard = card; - } - } - Server_Card *takeStashedCard() - { - Server_Card *oldStashedCard = stashedCard; - stashedCard = nullptr; - return oldStashedCard; - } - - void resetState(bool keepAnnotations = false); - QString setAttribute(CardAttribute attribute, const QString &avalue, bool allCards); - QString setAttribute(CardAttribute attribute, const QString &avalue, Event_SetCardAttr *event = nullptr); - - void getInfo(ServerInfo_Card *info); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.h deleted file mode 100644 index 77fea54ca..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_cardzone.h +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Max-Wilhelm Bruker * - * brukie@laptop * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef SERVER_CARDZONE_H -#define SERVER_CARDZONE_H - -#include -#include -#include -#include - -class Server_Card; -class Server_AbstractPlayer; -class Server_AbstractParticipant; -class Server_Game; -class GameEventStorage; - -class Server_CardZone -{ -private: - Server_AbstractPlayer *player; - QString name; - bool has_coords; // having coords means this zone has x and y coordinates - ServerInfo_Zone::ZoneType type; - int cardsBeingLookedAt; - QSet playersWithWritePermission; - bool alwaysRevealTopCard; - bool alwaysLookAtTopCard; - QList cards; - QMap> coordinateMap; // y -> (x -> card) - QMap> freePilesMap; // y -> (cardName -> x) - QMap freeSpaceMap; // y -> x - void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY); - void insertCardIntoCoordMap(Server_Card *card, int x, int y); - -public: - Server_CardZone(Server_AbstractPlayer *_player, - const QString &_name, - bool _has_coords, - ServerInfo_Zone::ZoneType _type); - ~Server_CardZone(); - - [[nodiscard]] const QList &getCards() const - { - return cards; - } - int removeCard(Server_Card *card); - int removeCard(Server_Card *card, bool &wasLookedAt); - Server_Card *getCard(int id, int *position = nullptr, bool remove = false); - - [[nodiscard]] int getCardsBeingLookedAt() const - { - return cardsBeingLookedAt; - } - void setCardsBeingLookedAt(int _cardsBeingLookedAt) - { - cardsBeingLookedAt = qMax(0, _cardsBeingLookedAt); - } - [[nodiscard]] bool isCardAtPosLookedAt(int pos) const; - [[nodiscard]] bool hasCoords() const - { - return has_coords; - } - [[nodiscard]] ServerInfo_Zone::ZoneType getType() const - { - return type; - } - [[nodiscard]] QString getName() const - { - return name; - } - [[nodiscard]] Server_AbstractPlayer *getPlayer() const - { - return player; - } - void getInfo(ServerInfo_Zone *info, Server_AbstractParticipant *recipient, bool omniscient); - - [[nodiscard]] int getFreeGridColumn(int x, int y, const QString &cardName, bool dontStackSameName) const; - [[nodiscard]] bool isColumnEmpty(int x, int y) const; - [[nodiscard]] bool isColumnStacked(int x, int y) const; - void fixFreeSpaces(GameEventStorage &ges); - void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y); - void insertCard(Server_Card *card, int x, int y); - void updateCardCoordinates(Server_Card *card, int oldX, int oldY); - void shuffle(int start = 0, int end = -1); - void clear(); - void addWritePermission(int playerId); - [[nodiscard]] const QSet &getPlayersWithWritePermission() const - { - return playersWithWritePermission; - } - [[nodiscard]] bool getAlwaysRevealTopCard() const - { - return alwaysRevealTopCard; - } - void setAlwaysRevealTopCard(bool _alwaysRevealTopCard) - { - alwaysRevealTopCard = _alwaysRevealTopCard; - } - [[nodiscard]] bool getAlwaysLookAtTopCard() const - { - return alwaysLookAtTopCard; - } - void setAlwaysLookAtTopCard(bool _alwaysLookAtTopCard) - { - alwaysLookAtTopCard = _alwaysLookAtTopCard; - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp deleted file mode 100644 index 2224ddb13..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.cpp +++ /dev/null @@ -1,872 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Max-Wilhelm Bruker * - * brukie@laptop * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#include "server_game.h" - -#include "../server.h" -#include "../server_database_interface.h" -#include "../server_protocolhandler.h" -#include "../server_room.h" -#include "libcockatrice/protocol/pb/command_move_card.pb.h" -#include "server_abstract_player.h" -#include "server_arrow.h" -#include "server_card.h" -#include "server_cardzone.h" -#include "server_player.h" -#include "server_spectator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, - int _gameId, - const QString &_description, - const QString &_password, - int _maxPlayers, - const QList &_gameTypes, - bool _onlyBuddies, - bool _onlyRegistered, - bool _spectatorsAllowed, - bool _spectatorsNeedPassword, - bool _spectatorsCanTalk, - bool _spectatorsSeeEverything, - int _startingLifeTotal, - bool _shareDecklistsOnLoad, - Server_Room *_room) - : QObject(), room(_room), nextPlayerId(0), hostId(0), creatorInfo(new ServerInfo_User(_creatorInfo)), - gameStarted(false), gameClosed(false), gameId(_gameId), password(_password), maxPlayers(_maxPlayers), - gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies), - onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed), - spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), - spectatorsSeeEverything(_spectatorsSeeEverything), startingLifeTotal(_startingLifeTotal), - shareDecklistsOnLoad(_shareDecklistsOnLoad), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), - firstGameStarted(false), turnOrderReversed(false), startTime(QDateTime::currentDateTime()), pingClock(nullptr), - gameMutex() -{ - currentReplay = new GameReplay; - currentReplay->set_replay_id(room->getServer()->getDatabaseInterface()->getNextReplayId()); - description = _description.simplified(); - - connect(this, &Server_Game::sigStartGameIfReady, this, &Server_Game::doStartGameIfReady, Qt::QueuedConnection); - - getInfo(*currentReplay->mutable_game_info()); - - if (room->getServer()->getGameShouldPing()) { - pingClock = new QTimer(this); - connect(pingClock, &QTimer::timeout, this, &Server_Game::pingClockTimeout); - pingClock->start(1000); - } -} - -Server_Game::~Server_Game() -{ - room->gamesLock.lockForWrite(); - gameMutex.lock(); - - gameClosed = true; - sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); - for (auto *participant : participants.values()) { - participant->prepareDestroy(); - } - participants.clear(); - - room->removeGame(this); - delete creatorInfo; - creatorInfo = 0; - - gameMutex.unlock(); - room->gamesLock.unlock(); - currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); - replayList.append(currentReplay); - storeGameInformation(); - - for (auto *replay : replayList) { - delete replay; - } - replayList.clear(); - - room = nullptr; - currentReplay = nullptr; - creatorInfo = nullptr; - - if (pingClock) { - delete pingClock; - pingClock = nullptr; - } - - qDebug() << "Server_Game destructor: gameId=" << gameId; - deleteLater(); -} - -void Server_Game::storeGameInformation() -{ - const ServerInfo_Game &gameInfo = replayList.first()->game_info(); - - Event_ReplayAdded replayEvent; - ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info(); - replayMatchInfo->set_game_id(gameInfo.game_id()); - replayMatchInfo->set_room_name(room->getName().toStdString()); - replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toSecsSinceEpoch()); - replayMatchInfo->set_length(secondsElapsed); - replayMatchInfo->set_game_name(gameInfo.description()); - - const QStringList &allGameTypes = room->getGameTypes(); - QStringList _gameTypes; - for (int i = gameInfo.game_types_size() - 1; i >= 0; --i) - _gameTypes.append(allGameTypes[gameInfo.game_types(i)]); - - for (const auto &playerName : allPlayersEver) { - replayMatchInfo->add_player_names(playerName.toStdString()); - } - - for (int i = 0; i < replayList.size(); ++i) { - ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list(); - replayInfo->set_replay_id(replayList[i]->replay_id()); - replayInfo->set_replay_name(gameInfo.description()); - replayInfo->set_duration(replayList[i]->duration_seconds()); - } - - SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); - Server *server = room->getServer(); - server->clientsLock.lockForRead(); - for (auto userName : allPlayersEver + allSpectatorsEver) { - Server_AbstractUserInterface *userHandler = server->findUser(userName); - if (userHandler && server->getStoreReplaysEnabled()) - userHandler->sendProtocolItem(*sessionEvent); - } - server->clientsLock.unlock(); - delete sessionEvent; - - if (server->getStoreReplaysEnabled()) { - server->getDatabaseInterface()->storeGameInformation(room->getName(), _gameTypes, gameInfo, allPlayersEver, - allSpectatorsEver, replayList); - } -} - -void Server_Game::pingClockTimeout() -{ - QMutexLocker locker(&gameMutex); - ++secondsElapsed; - - GameEventStorage ges; - ges.setGameEventContext(Context_PingChanged()); - - bool allPlayersInactive = true; - int playerCount = 0; - for (auto *participant : participants) { - if (participant == nullptr) - continue; - - if (!participant->isSpectator()) { - ++playerCount; - } - - if (participant->updatePingTime()) { - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_ping_seconds(participant->getPingTime()); - ges.enqueueGameEvent(event, participant->getPlayerId()); - } - - if ((participant->getPingTime() != -1) && - (!participant->isSpectator() || participant->getPlayerId() == hostId)) { - allPlayersInactive = false; - } - } - ges.sendToGame(this); - - const int maxTime = room->getServer()->getMaxGameInactivityTime(); - if (allPlayersInactive) { - if (((maxTime > 0) && (++inactivityCounter >= maxTime)) || (playerCount < maxPlayers)) { - deleteLater(); - } - } else { - inactivityCounter = 0; - } -} - -QMap Server_Game::getPlayers() const // copies pointers to new map -{ - QMap players; - QMutexLocker locker(&gameMutex); - for (int id : participants.keys()) { - auto *participant = participants[id]; - if (!participant->isSpectator()) { - players[id] = static_cast(participant); - } - } - return players; -} - -Server_AbstractPlayer *Server_Game::getPlayer(int id) const -{ - auto *participant = participants.value(id); - if (participant && !participant->isSpectator()) { - return static_cast(participant); - } else { - return nullptr; - } -} - -int Server_Game::getPlayerCount() const -{ - return participants.size() - getSpectatorCount(); -} - -int Server_Game::getSpectatorCount() const -{ - QMutexLocker locker(&gameMutex); - - int result = 0; - for (Server_AbstractParticipant *participant : participants.values()) { - if (participant->isSpectator()) - ++result; - } - return result; -} - -void Server_Game::createGameStateChangedEvent(Event_GameStateChanged *event, - Server_AbstractParticipant *recipient, - bool omniscient, - bool withUserInfo) -{ - event->set_seconds_elapsed(secondsElapsed); - if (gameStarted) { - event->set_game_started(true); - event->set_active_player_id(0); - event->set_active_phase(0); - } else - event->set_game_started(false); - - for (Server_AbstractParticipant *participant : participants.values()) { - participant->getInfo(event->add_player_list(), recipient, omniscient, withUserInfo); - } -} - -void Server_Game::sendGameStateToPlayers() -{ - // game state information for replay and omniscient spectators - Event_GameStateChanged omniscientEvent; - createGameStateChangedEvent(&omniscientEvent, nullptr, true, false); - - GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); - replayCont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); - replayCont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*replayCont); - delete replayCont; - - // If spectators are not omniscient, we need an additional createGameStateChangedEvent call, otherwise we can use - // the data we used for the replay. All spectators are equal, so we don't need to make a createGameStateChangedEvent - // call for each one. - Event_GameStateChanged spectatorNormalEvent; - createGameStateChangedEvent(&spectatorNormalEvent, nullptr, false, false); - - // send game state info to clients according to their role in the game - for (auto *participant : participants.values()) { - GameEventContainer *gec; - if (participant->isSpectator()) { - if (spectatorsSeeEverything || participant->isJudge()) { - gec = prepareGameEvent(omniscientEvent, -1); - } else { - gec = prepareGameEvent(spectatorNormalEvent, -1); - } - } else { - Event_GameStateChanged event; - createGameStateChangedEvent(&event, participant, false, false); - - gec = prepareGameEvent(event, -1); - } - participant->sendGameEvent(*gec); - delete gec; - } -} - -void Server_Game::doStartGameIfReady(bool forceStartGame) -{ - Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); - QMutexLocker locker(&gameMutex); - - if (getPlayerCount() < maxPlayers && !forceStartGame) { - return; - } - - auto players = getPlayers(); - for (auto *player : players.values()) { - if (!player->getReadyStart()) { - if (forceStartGame) { - // Player is not ready to start, so kick them - // TODO: Move them to Spectators instead - kickParticipant(player->getPlayerId()); - } else { - return; - } - } - } - - players = getPlayers(); // players could have been kicked, get new list of players - for (Server_AbstractPlayer *player : players.values()) { - player->setupZones(); - } - - gameStarted = true; - for (auto *player : players.values()) { - player->setConceded(false); - player->setReadyStart(false); - } - - if (firstGameStarted) { - currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); - replayList.append(currentReplay); - currentReplay = new GameReplay; - currentReplay->set_replay_id(databaseInterface->getNextReplayId()); - ServerInfo_Game *gameInfo = currentReplay->mutable_game_info(); - getInfo(*gameInfo); - gameInfo->set_started(false); - - Event_GameStateChanged omniscientEvent; - createGameStateChangedEvent(&omniscientEvent, nullptr, true, true); - - GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); - replayCont->set_seconds_elapsed(0); - replayCont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*replayCont); - delete replayCont; - - startTimeOfThisGame = secondsElapsed; - } else - firstGameStarted = true; - - sendGameStateToPlayers(); - - activePlayer = -1; - nextTurn(); - - locker.unlock(); - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_started(true); - emit gameInfoChanged(gameInfo); -} - -void Server_Game::startGameIfReady(bool forceStartGame) -{ - emit sigStartGameIfReady(forceStartGame); -} - -void Server_Game::stopGameIfFinished() -{ - QMutexLocker locker(&gameMutex); - - int playing = 0; - auto players = getPlayers(); - for (auto *player : players.values()) { - if (!player->getConceded()) - ++playing; - } - if (playing > 1) - return; - - gameStarted = false; - - for (auto *player : players.values()) { - player->clearZones(); - player->setConceded(false); - } - - sendGameStateToPlayers(); - - locker.unlock(); - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_started(false); - emit gameInfoChanged(gameInfo); -} - -Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user, - const QString &_password, - bool spectator, - bool overrideRestrictions, - bool asJudge) -{ - Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); - for (auto *participant : participants.values()) { - if (participant->getUserInfo()->name() == user->name()) - return Response::RespContextError; - } - - if (asJudge && !(user->user_level() & ServerInfo_User::IsJudge)) { - return Response::RespUserLevelTooLow; - } - if (!(overrideRestrictions && (user->user_level() & ServerInfo_User::IsModerator))) { - if ((_password != password) && !(spectator && !spectatorsNeedPassword)) - return Response::RespWrongPassword; - if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered) - return Response::RespUserLevelTooLow; - if (onlyBuddies && (user->name() != creatorInfo->name())) - if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), - QString::fromStdString(user->name()))) - return Response::RespOnlyBuddies; - if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), - QString::fromStdString(user->name()))) - return Response::RespInIgnoreList; - if (spectator) { - if (!spectatorsAllowed) - return Response::RespSpectatorsNotAllowed; - } - } - if (!spectator && (gameStarted || (getPlayerCount() >= getMaxPlayers()))) - return Response::RespGameFull; - - return Response::RespOk; -} - -bool Server_Game::containsUser(const QString &userName) const -{ - QMutexLocker locker(&gameMutex); - - for (auto *participant : participants.values()) { - if (participant->getUserInfo()->name() == userName.toStdString()) - return true; - } - return false; -} - -void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, - ResponseContainer &rc, - bool spectator, - bool judge, - bool broadcastUpdate) -{ - QMutexLocker locker(&gameMutex); - - Server_AbstractParticipant *newParticipant; - if (spectator) { - newParticipant = new Server_Spectator(this, nextPlayerId++, userInterface->copyUserInfo(true, true, true), - judge, userInterface); - } else { - newParticipant = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true, true), judge, - userInterface); - } - - newParticipant->moveToThread(thread()); - - Event_Join joinEvent; - newParticipant->getProperties(*joinEvent.mutable_player_properties(), true); - sendGameEventContainer(prepareGameEvent(joinEvent, -1)); - - const QString playerName = QString::fromStdString(newParticipant->getUserInfo()->name()); - participants.insert(newParticipant->getPlayerId(), newParticipant); - if (spectator) { - allSpectatorsEver.insert(playerName); - } else { - allPlayersEver.insert(playerName); - - // if the original creator of the game joins, give them host status back - //! \todo transferring host to spectators has side effects - if (newParticipant->getUserInfo()->name() == creatorInfo->name()) { - hostId = newParticipant->getPlayerId(); - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); - } - } - - if (broadcastUpdate) { - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_player_count(getPlayerCount()); - gameInfo.set_spectators_count(getSpectatorCount()); - emit gameInfoChanged(gameInfo); - } - - if ((newParticipant->getUserInfo()->user_level() & ServerInfo_User::IsRegistered) && !spectator) - room->getServer()->addPersistentPlayer(playerName, room->getId(), gameId, newParticipant->getPlayerId()); - - userInterface->playerAddedToGame(gameId, room->getId(), newParticipant->getPlayerId()); - - createGameJoinedEvent(newParticipant, rc, false); -} - -void Server_Game::removeParticipant(Server_AbstractParticipant *participant, Event_Leave::LeaveReason reason) -{ - room->getServer()->removePersistentPlayer(QString::fromStdString(participant->getUserInfo()->name()), room->getId(), - gameId, participant->getPlayerId()); - participants.remove(participant->getPlayerId()); - - bool spectator = participant->isSpectator(); - GameEventStorage ges; - if (!spectator) { - auto *player = static_cast(participant); - removeArrowsRelatedToPlayer(ges, player); - unattachCards(ges, player); - } - - Event_Leave event; - event.set_reason(reason); - ges.enqueueGameEvent(event, participant->getPlayerId()); - ges.sendToGame(this); - - bool playerActive = activePlayer == participant->getPlayerId(); - bool playerHost = hostId == participant->getPlayerId(); - participant->prepareDestroy(); - - if (playerHost) { - int newHostId = -1; - for (auto *otherPlayer : getPlayers().values()) { - newHostId = otherPlayer->getPlayerId(); - break; - } - if (newHostId != -1) { - hostId = newHostId; - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); - } else { - gameClosed = true; - deleteLater(); - return; - } - } - if (!spectator) { - stopGameIfFinished(); - if (gameStarted && playerActive) - nextTurn(); - } - - ServerInfo_Game gameInfo; - gameInfo.set_room_id(room->getId()); - gameInfo.set_game_id(gameId); - gameInfo.set_player_count(getPlayerCount()); - gameInfo.set_spectators_count(getSpectatorCount()); - emit gameInfoChanged(gameInfo); -} - -void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_AbstractPlayer *player) -{ - QMutexLocker locker(&gameMutex); - - // Remove all arrows of other players pointing to the player being removed or to one of his cards. - // Also remove all arrows starting at one of his cards. This is necessary since players can create - // arrows that start at another person's cards. - for (Server_AbstractPlayer *anyPlayer : getPlayers().values()) { - QList toDelete; - for (auto *arrow : anyPlayer->getArrows().values()) { - auto *targetCard = qobject_cast(arrow->getTargetItem()); - if (targetCard) { - if (targetCard->getZone() != nullptr && targetCard->getZone()->getPlayer() == player) - toDelete.append(arrow); - } else if (arrow->getTargetItem() == player) { - toDelete.append(arrow); - } - - // Don't use else here! It has to happen regardless of whether targetCard == 0. - if (arrow->getStartCard()->getZone() != nullptr && arrow->getStartCard()->getZone()->getPlayer() == player) - toDelete.append(arrow); - } - for (auto *arrow : toDelete) { - Event_DeleteArrow event; - event.set_arrow_id(arrow->getId()); - ges.enqueueGameEvent(event, anyPlayer->getPlayerId()); - - anyPlayer->deleteArrow(arrow->getId()); - } - } -} - -void Server_Game::unattachCards(GameEventStorage &ges, Server_AbstractPlayer *player) -{ - QMutexLocker locker(&gameMutex); - - for (auto zone : player->getZones()) { - for (auto card : zone->getCards()) { - // Make a copy of the list because the original one gets modified during the loop - QList attachedCards = card->getAttachedCards(); - for (Server_Card *attachedCard : attachedCards) { - auto otherPlayer = attachedCard->getZone()->getPlayer(); - // do not modify the current player's zone! - // this would cause the current card iterator to be invalidated! - // we only have to return cards owned by other players - // because the current player is leaving the game anyway - if (otherPlayer != player) { - otherPlayer->unattachCard(ges, attachedCard); - } - } - } - } -} - -bool Server_Game::kickParticipant(int playerId) -{ - QMutexLocker locker(&gameMutex); - - auto *participant = participants.value(playerId); - if (!participant) - return false; - - GameEventContainer *gec = prepareGameEvent(Event_Kicked(), -1); - participant->sendGameEvent(*gec); - delete gec; - - removeParticipant(participant, Event_Leave::USER_KICKED); - - return true; -} - -void Server_Game::setActivePlayer(int _activePlayer) -{ - QMutexLocker locker(&gameMutex); - - removeArrows(0, true); - - activePlayer = _activePlayer; - - Event_SetActivePlayer event; - event.set_active_player_id(activePlayer); - sendGameEventContainer(prepareGameEvent(event, -1)); - - setActivePhase(0); -} - -void Server_Game::setActivePhase(int newPhase) -{ - QMutexLocker locker(&gameMutex); - - removeArrows(newPhase); - activePhase = newPhase; - - Event_SetActivePhase event; - event.set_phase(activePhase); - sendGameEventContainer(prepareGameEvent(event, -1)); -} - -void Server_Game::removeArrows(int newPhase, bool force) -{ - QMutexLocker locker(&gameMutex); - - for (auto *anyPlayer : getPlayers().values()) { - for (auto *arrowToDelete : anyPlayer->getArrows().values()) { // values creates a copy - if (force || arrowToDelete->checkPhaseDeletion(newPhase)) { - Event_DeleteArrow event; - event.set_arrow_id(arrowToDelete->getId()); - sendGameEventContainer(prepareGameEvent(event, anyPlayer->getPlayerId())); - - anyPlayer->deleteArrow(arrowToDelete->getId()); - } - } - } -} - -void Server_Game::nextTurn() -{ - QMutexLocker locker(&gameMutex); - - if (participants.isEmpty()) { - qWarning() << "Server_Game::nextTurn was called while players is empty; gameId = " << gameId; - return; - } - - auto players = getPlayers(); - const QList keys = players.keys(); - int listPos = -1; - if (activePlayer != -1) { - listPos = keys.indexOf(activePlayer); - } - do { - if (turnOrderReversed) { - --listPos; - if (listPos < 0) { - listPos = keys.size() - 1; - } - } else { - ++listPos; - if (listPos == keys.size()) { - listPos = 0; - } - } - } while (players.value(keys[listPos])->getConceded()); - - setActivePlayer(keys[listPos]); -} - -void Server_Game::createGameJoinedEvent(Server_AbstractParticipant *joiningParticipant, - ResponseContainer &rc, - bool resuming) -{ - Event_GameJoined event1; - getInfo(*event1.mutable_game_info()); - event1.set_host_id(hostId); - event1.set_player_id(joiningParticipant->getPlayerId()); - event1.set_spectator(joiningParticipant->isSpectator()); - event1.set_judge(joiningParticipant->isJudge()); - event1.set_resuming(resuming); - if (resuming) { - const QStringList &allGameTypes = room->getGameTypes(); - for (int i = 0; i < allGameTypes.size(); ++i) { - ServerInfo_GameType *newGameType = event1.add_game_types(); - newGameType->set_game_type_id(i); - newGameType->set_description(allGameTypes[i].toStdString()); - } - } - rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1)); - - Event_GameStateChanged event2; - event2.set_seconds_elapsed(secondsElapsed); - event2.set_game_started(gameStarted); - event2.set_active_player_id(activePlayer); - event2.set_active_phase(activePhase); - - bool omniscient = joiningParticipant->isSpectator() && (spectatorsSeeEverything || joiningParticipant->isJudge()); - for (auto *participant : participants.values()) { - participant->getInfo(event2.add_player_list(), joiningParticipant, omniscient, true); - } - - rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1)); -} - -void Server_Game::sendGameEventContainer(GameEventContainer *cont, - GameEventStorageItem::EventRecipients recipients, - int privatePlayerId) -{ - QMutexLocker locker(&gameMutex); - - cont->set_game_id(gameId); - for (auto *participant : participants.values()) { - const bool playerPrivate = (participant->getPlayerId() == privatePlayerId) || - (participant->isSpectator() && (spectatorsSeeEverything || participant->isJudge())); - if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) || - (recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate)) - participant->sendGameEvent(*cont); - } - if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) { - cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); - cont->clear_game_id(); - currentReplay->add_event_list()->CopyFrom(*cont); - } - - delete cont; -} - -GameEventContainer * -Server_Game::prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context) -{ - auto *cont = new GameEventContainer; - cont->set_game_id(gameId); - if (context) - cont->mutable_context()->CopyFrom(*context); - GameEvent *event = cont->add_event_list(); - if (playerId != -1) - event->set_player_id(playerId); - event->GetReflection() - ->MutableMessage(event, gameEvent.GetDescriptor()->FindExtensionByName("ext")) - ->CopyFrom(gameEvent); - return cont; -} - -void Server_Game::getInfo(ServerInfo_Game &result) const -{ - QMutexLocker locker(&gameMutex); - - result.set_room_id(room->getId()); - result.set_game_id(gameId); - if (gameClosed) { - result.set_closed(true); - } else { - for (auto type : gameTypes) { - result.add_game_types(type); - } - - result.set_max_players(getMaxPlayers()); - result.set_description(getDescription().toStdString()); - result.set_with_password(!getPassword().isEmpty()); - result.set_player_count(getPlayerCount()); - result.set_started(gameStarted); - result.mutable_creator_info()->CopyFrom(*getCreatorInfo()); - result.set_only_buddies(onlyBuddies); - result.set_only_registered(onlyRegistered); - result.set_spectators_allowed(getSpectatorsAllowed()); - result.set_spectators_need_password(getSpectatorsNeedPassword()); - result.set_spectators_can_chat(spectatorsCanTalk); - result.set_spectators_omniscient(spectatorsSeeEverything); - result.set_share_decklists_on_load(shareDecklistsOnLoad); - result.set_spectators_count(getSpectatorCount()); - result.set_start_time(startTime.toSecsSinceEpoch()); - } -} - -void Server_Game::returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPlayer *player) -{ - QMutexLocker locker(&gameMutex); - // Return cards to their rightful owners before conceding the game - static const QRegularExpression ownerRegex{"Owner: ?([^\n]+)"}; - const auto &playerTable = player->getZones().value(ZoneNames::TABLE); - for (const auto &card : playerTable->getCards()) { - if (card == nullptr) { - continue; - } - - const auto ®exResult = ownerRegex.match(card->getAnnotation()); - if (!regexResult.hasMatch()) { - continue; - } - - CardToMove cardToMove; - cardToMove.set_card_id(card->getId()); - - for (const auto *otherPlayer : getPlayers()) { - if (otherPlayer == nullptr || otherPlayer->getUserInfo() == nullptr) { - continue; - } - - const auto &ownerToReturnTo = regexResult.captured(1); - const auto &correctOwner = QString::compare(QString::fromStdString(otherPlayer->getUserInfo()->name()), - ownerToReturnTo, Qt::CaseInsensitive) == 0; - if (!correctOwner) { - continue; - } - - const auto &targetZone = otherPlayer->getZones().value(ZoneNames::TABLE); - - if (playerTable == nullptr || targetZone == nullptr) { - continue; - } - - player->moveCard(ges, playerTable, QList() << &cardToMove, targetZone, 0, 0, false); - break; - } - } -} diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h deleted file mode 100644 index 1c658f2ba..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_game.h +++ /dev/null @@ -1,224 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Max-Wilhelm Bruker * - * brukie@laptop * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ -#ifndef SERVERGAME_H -#define SERVERGAME_H - -#include "../server_response_containers.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class QTimer; -class GameEventContainer; -class GameReplay; -class Server_Room; -class Server_AbstractPlayer; -class Server_AbstractParticipant; -class ServerInfo_User; -class ServerInfo_Game; -class Server_AbstractUserInterface; -class Event_GameStateChanged; - -class Server_Game : public QObject -{ - Q_OBJECT -private: - Server_Room *room; - int nextPlayerId; - int hostId; - ServerInfo_User *creatorInfo; - QMap participants; - QSet allPlayersEver, allSpectatorsEver; - bool gameStarted; - bool gameClosed; - int gameId; - QString description; - QString password; - int maxPlayers; - QList gameTypes; - int activePlayer, activePhase; - bool onlyBuddies, onlyRegistered; - bool spectatorsAllowed; - bool spectatorsNeedPassword; - bool spectatorsCanTalk; - bool spectatorsSeeEverything; - int startingLifeTotal; - bool shareDecklistsOnLoad; - int inactivityCounter; - int startTimeOfThisGame, secondsElapsed; - bool firstGameStarted; - bool turnOrderReversed; - QDateTime startTime; - QTimer *pingClock; - QList replayList; - GameReplay *currentReplay; - - void createGameStateChangedEvent(Event_GameStateChanged *event, - Server_AbstractParticipant *recipient, - bool omniscient, - bool withUserInfo); - void storeGameInformation(); -signals: - void sigStartGameIfReady(bool override); - void gameInfoChanged(ServerInfo_Game gameInfo); -private slots: - void pingClockTimeout(); - void doStartGameIfReady(bool forceStartGame = false); - -public: - mutable QRecursiveMutex gameMutex; - Server_Game(const ServerInfo_User &_creatorInfo, - int _gameId, - const QString &_description, - const QString &_password, - int _maxPlayers, - const QList &_gameTypes, - bool _onlyBuddies, - bool _onlyRegistered, - bool _spectatorsAllowed, - bool _spectatorsNeedPassword, - bool _spectatorsCanTalk, - bool _spectatorsSeeEverything, - int _startingLifeTotal, - bool _shareDecklistsOnLoad, - Server_Room *parent); - ~Server_Game() override; - Server_Room *getRoom() const - { - return room; - } - void getInfo(ServerInfo_Game &result) const; - int getHostId() const - { - return hostId; - } - ServerInfo_User *getCreatorInfo() const - { - return creatorInfo; - } - bool getGameStarted() const - { - return gameStarted; - } - int getPlayerCount() const; - int getSpectatorCount() const; - QMap getPlayers() const; - Server_AbstractPlayer *getPlayer(int id) const; - const QMap &getParticipants() const - { - return participants; - } - int getGameId() const - { - return gameId; - } - QString getDescription() const - { - return description; - } - QString getPassword() const - { - return password; - } - int getMaxPlayers() const - { - return maxPlayers; - } - bool getSpectatorsAllowed() const - { - return spectatorsAllowed; - } - bool getSpectatorsNeedPassword() const - { - return spectatorsNeedPassword; - } - bool getSpectatorsCanTalk() const - { - return spectatorsCanTalk; - } - bool getSpectatorsSeeEverything() const - { - return spectatorsSeeEverything; - } - int getStartingLifeTotal() const - { - return startingLifeTotal; - } - bool getShareDecklistsOnLoad() const - { - return shareDecklistsOnLoad; - } - Response::ResponseCode - checkJoin(ServerInfo_User *user, const QString &_password, bool spectator, bool overrideRestrictions, bool asJudge); - bool containsUser(const QString &userName) const; - void addPlayer(Server_AbstractUserInterface *userInterface, - ResponseContainer &rc, - bool spectator, - bool judge, - bool broadcastUpdate = true); - void removeParticipant(Server_AbstractParticipant *participant, Event_Leave::LeaveReason reason); - void removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_AbstractPlayer *player); - void unattachCards(GameEventStorage &ges, Server_AbstractPlayer *player); - bool kickParticipant(int playerId); - void startGameIfReady(bool forceStartGame); - void stopGameIfFinished(); - int getActivePlayer() const - { - return activePlayer; - } - int getActivePhase() const - { - return activePhase; - } - void setActivePlayer(int newPlayer); - void setActivePhase(int newPhase); - void removeArrows(int newPhase, bool force = false); - void nextTurn(); - int getSecondsElapsed() const - { - return secondsElapsed; - } - bool reverseTurnOrder() - { - return turnOrderReversed = !turnOrderReversed; - } - - void createGameJoinedEvent(Server_AbstractParticipant *participant, ResponseContainer &rc, bool resuming); - - GameEventContainer * - prepareGameEvent(const ::google::protobuf::Message &gameEvent, int playerId, GameEventContext *context = 0); - GameEventContext prepareGameEventContext(const ::google::protobuf::Message &gameEventContext); - - void sendGameStateToPlayers(); - void sendGameEventContainer(GameEventContainer *cont, - GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | - GameEventStorageItem::SendToOthers, - int privatePlayerId = -1); - void returnCardsFromPlayer(GameEventStorage &ges, Server_AbstractPlayer *player); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_move_card_struct.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_move_card_struct.h deleted file mode 100644 index c5b293b6d..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_move_card_struct.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef MOVE_CARD_STRUCT -#define MOVE_CARD_STRUCT - -#include "server_card.h" -class CardToMove; - -struct MoveCardStruct -{ - Server_Card *card; - int position; - const CardToMove *cardToMove; - int xCoord, yCoord; - MoveCardStruct(Server_Card *_card, int _position, const CardToMove *_cardToMove) - : card(_card), position(_position), cardToMove(_cardToMove), xCoord(_card->getX()), yCoord(_card->getY()) - - { - } - bool operator<(const MoveCardStruct &other) const - { - return (yCoord == other.yCoord && - ((xCoord == other.xCoord && position < other.position) || xCoord < other.xCoord)) || - yCoord < other.yCoord; - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp deleted file mode 100644 index 1175e4b57..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.cpp +++ /dev/null @@ -1,598 +0,0 @@ -#include "server_player.h" - -#include "../server.h" -#include "../server_abstractuserinterface.h" -#include "../server_database_interface.h" -#include "../server_room.h" -#include "server_card.h" -#include "server_cardzone.h" -#include "server_counter.h" -#include "server_game.h" -#include "server_move_card_struct.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Server_Player::Server_Player(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_userInterface) - : Server_AbstractPlayer(_game, _playerId, _userInfo, _judge, _userInterface) -{ -} - -Server_Player::~Server_Player() = default; - -int Server_Player::newCounterId() const -{ - int id = 0; - QMapIterator i(counters); - while (i.hasNext()) { - Server_Counter *c = i.next().value(); - if (c->getId() > id) { - id = c->getId(); - } - } - return id + 1; -} - -void Server_Player::setupZones() -{ - Server_AbstractPlayer::setupZones(); - - // This may need to be customized according to the game rules. - // ------------------------------------------------------------------ - - // Create zones - auto *deckZone = new Server_CardZone(this, ZoneNames::DECK, false, ServerInfo_Zone::HiddenZone); - addZone(deckZone); - auto *sbZone = new Server_CardZone(this, ZoneNames::SIDEBOARD, false, ServerInfo_Zone::HiddenZone); - addZone(sbZone); - addZone(new Server_CardZone(this, ZoneNames::TABLE, true, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, ZoneNames::HAND, false, ServerInfo_Zone::PrivateZone)); - addZone(new Server_CardZone(this, ZoneNames::STACK, false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, ZoneNames::GRAVE, false, ServerInfo_Zone::PublicZone)); - addZone(new Server_CardZone(this, ZoneNames::EXILE, false, ServerInfo_Zone::PublicZone)); - - addCounter(new Server_Counter(0, "life", makeColor(255, 255, 255), 25, game->getStartingLifeTotal())); - addCounter(new Server_Counter(1, "w", makeColor(255, 255, 150), 20, 0)); - addCounter(new Server_Counter(2, "u", makeColor(150, 150, 255), 20, 0)); - addCounter(new Server_Counter(3, "b", makeColor(150, 150, 150), 20, 0)); - addCounter(new Server_Counter(4, "r", makeColor(250, 150, 150), 20, 0)); - addCounter(new Server_Counter(5, "g", makeColor(150, 255, 150), 20, 0)); - addCounter(new Server_Counter(6, "x", makeColor(255, 255, 255), 20, 0)); - addCounter(new Server_Counter(7, "storm", makeColor(255, 150, 30), 20, 0)); - - // ------------------------------------------------------------------ - - // Assign card ids and create deck from deck list - auto insertCardsIntoZone = [this](auto cards, auto *zone) { - for (auto card : cards) { - for (int k = 0; k < card->getNumber(); ++k) { - zone->insertCard(new Server_Card(card->toCardRef(), nextCardId++, 0, 0, zone), -1, 0); - } - } - }; - - insertCardsIntoZone(deck->getCardNodes({DECK_ZONE_MAIN}), deckZone); - insertCardsIntoZone(deck->getCardNodes({DECK_ZONE_SIDE}), sbZone); - - const QList &sideboardPlan = deck->getCurrentSideboardPlan(); - for (const auto &m : sideboardPlan) { - const QString startZone = nameFromStdString(m.start_zone()); - const QString targetZone = nameFromStdString(m.target_zone()); - - Server_CardZone *start, *target; - if (startZone == DECK_ZONE_MAIN) { - start = deckZone; - } else if (startZone == DECK_ZONE_SIDE) { - start = sbZone; - } else { - continue; - } - if (targetZone == DECK_ZONE_MAIN) { - target = deckZone; - } else if (targetZone == DECK_ZONE_SIDE) { - target = sbZone; - } else { - continue; - } - - for (int j = 0; j < start->getCards().size(); ++j) { - if (start->getCards()[j]->getName() == nameFromStdString(m.card_name())) { - Server_Card *card = start->getCard(j, nullptr, true); - target->insertCard(card, -1, 0); - break; - } - } - } - - deckZone->shuffle(); -} - -void Server_Player::clearZones() -{ - Server_AbstractPlayer::clearZones(); - for (Server_Counter *counter : counters) { - delete counter; - } - counters.clear(); - - lastDrawList.clear(); -} - -void Server_Player::addCounter(Server_Counter *counter) -{ - counters.insert(counter->getId(), counter); -} - -Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int number) -{ - Server_CardZone *deckZone = zones.value(ZoneNames::DECK); - Server_CardZone *handZone = zones.value(ZoneNames::HAND); - if (deckZone->getCards().size() < number) { - number = deckZone->getCards().size(); - } - - Event_DrawCards eventOthers; - eventOthers.set_number(number); - Event_DrawCards eventPrivate(eventOthers); - - for (int i = 0; i < number; ++i) { - Server_Card *card = deckZone->getCard(0, nullptr, true); - handZone->insertCard(card, -1, 0); - lastDrawList.append(card->getId()); - - ServerInfo_Card *cardInfo = eventPrivate.add_cards(); - cardInfo->set_id(card->getId()); - cardInfo->set_name(card->getName().toStdString()); - cardInfo->set_provider_id(card->getProviderId().toStdString()); - } - - ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId); - ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers); - - if (number > 0) { - revealTopCardIfNeeded(deckZone, ges); - int currentKnownCards = deckZone->getCardsBeingLookedAt(); - deckZone->setCardsBeingLookedAt(currentKnownCards - number); - } - - return Response::RespOk; -} - -void Server_Player::onCardBeingMoved(GameEventStorage &ges, - const MoveCardStruct &cardStruct, - Server_CardZone *startzone, - Server_CardZone *targetzone, - bool undoingDraw) -{ - Server_AbstractPlayer::onCardBeingMoved(ges, cardStruct, startzone, targetzone, undoingDraw); - - Server_Card *card = cardStruct.card; - - // "Undo draw" should only remain valid if the just-drawn card stays within the user's hand (e.g., they only - // reorder their hand). If a just-drawn card leaves the hand then remove cards before it from the list - // (Ignore the case where the card is currently being un-drawn.) - if (startzone->getName() == ZoneNames::HAND && targetzone->getName() != ZoneNames::HAND && !undoingDraw) { - int index = lastDrawList.lastIndexOf(card->getId()); - if (index != -1) { - lastDrawList.erase(lastDrawList.begin(), lastDrawList.begin() + index); - } - } -} - -Response::ResponseCode -Server_Player::cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - if (game->getGameStarted()) { - return Response::RespContextError; - } - - DeckList *newDeck; - if (cmd.has_deck_id()) { - try { - newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), - userInfo->id()); - } catch (Response::ResponseCode &r) { - return r; - } - } else { - newDeck = new DeckList(fileFromStdString(cmd.deck())); - } - - if (!newDeck) { - return Response::RespInternalError; - } - - delete deck; - deck = newDeck; - sideboardLocked = true; - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_sideboard_locked(true); - event.mutable_player_properties()->set_deck_hash(deck->getDeckHash().toStdString()); - ges.enqueueGameEvent(event, playerId); - - Context_DeckSelect context; - context.set_deck_hash(deck->getDeckHash().toStdString()); - context.set_sideboard_size(deck->getSideboardSize()); - if (game->getShareDecklistsOnLoad()) { - context.set_deck_list(deck->writeToString_Native().toStdString()); - } - ges.setGameEventContext(context); - - auto *re = new Response_DeckDownload; - re->set_deck(deck->writeToString_Native().toStdString()); - - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode Server_Player::cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - if (readyStart) { - return Response::RespContextError; - } - if (!deck) { - return Response::RespContextError; - } - if (sideboardLocked) { - return Response::RespContextError; - } - - QList sideboardPlan; - for (int i = 0; i < cmd.move_list_size(); ++i) { - sideboardPlan.append(cmd.move_list(i)); - } - deck->setCurrentSideboardPlan(sideboardPlan); - - return Response::RespOk; -} - -Response::ResponseCode Server_Player::cmdSetSideboardLock(const Command_SetSideboardLock &cmd, - ResponseContainer & /*rc*/, - GameEventStorage &ges) -{ - if (readyStart) { - return Response::RespContextError; - } - if (!deck) { - return Response::RespContextError; - } - if (sideboardLocked == cmd.locked()) { - return Response::RespContextError; - } - - sideboardLocked = cmd.locked(); - if (sideboardLocked) { - deck->setCurrentSideboardPlan(QList()); - } - - Event_PlayerPropertiesChanged event; - event.mutable_player_properties()->set_sideboard_locked(sideboardLocked); - ges.enqueueGameEvent(event, playerId); - ges.setGameEventContext(Context_SetSideboardLock()); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdShuffle(const Command_Shuffle &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - if (conceded) { - return Response::RespContextError; - } - - if (cmd.has_zone_name() && cmd.zone_name() != ZoneNames::DECK) { - return Response::RespFunctionNotAllowed; - } - - Server_CardZone *zone = zones.value(ZoneNames::DECK); - if (!zone) { - return Response::RespNameNotFound; - } - - zone->shuffle(cmd.start(), cmd.end()); - - Event_Shuffle event; - event.set_zone_name(zone->getName().toStdString()); - event.set_start(cmd.start()); - event.set_end(cmd.end()); - ges.enqueueGameEvent(event, playerId); - revealTopCardIfNeeded(zone, ges); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdMulligan(const Command_Mulligan &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_CardZone *hand = zones.value(ZoneNames::HAND); - Server_CardZone *_deck = zones.value(ZoneNames::DECK); - int number = cmd.number(); - - if (!hand->getCards().isEmpty()) { - auto cardsToMove = QList(); - for (auto &card : hand->getCards()) { - auto *cardToMove = new CardToMove; - cardToMove->set_card_id(card->getId()); - cardsToMove.append(cardToMove); - } - moveCard(ges, hand, cardsToMove, _deck, -1, 0, false); - qDeleteAll(cardsToMove); - } - - _deck->shuffle(); - ges.enqueueGameEvent(Event_Shuffle(), playerId); - - drawCards(ges, number); - - Context_Mulligan context; - context.set_number(static_cast(hand->getCards().size())); - ges.setGameEventContext(context); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - return drawCards(ges, cmd.number()); -} - -Response::ResponseCode -Server_Player::cmdUndoDraw(const Command_UndoDraw & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - if (lastDrawList.isEmpty()) { - return Response::RespContextError; - } - - Response::ResponseCode retVal; - auto *cardToMove = new CardToMove; - cardToMove->set_card_id(lastDrawList.takeLast()); - retVal = moveCard(ges, zones.value(ZoneNames::HAND), QList() << cardToMove, - zones.value(ZoneNames::DECK), 0, 0, false, true); - delete cardToMove; - - return retVal; -} - -Response::ResponseCode -Server_Player::cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_Counter *c = counters.value(cmd.counter_id(), 0); - if (!c) { - return Response::RespNameNotFound; - } - - c->setCount(c->getCount() + cmd.delta()); - - Event_SetCounter event; - event.set_counter_id(c->getId()); - event.set_value(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - auto *c = new Server_Counter(newCounterId(), nameFromStdString(cmd.counter_name()), cmd.counter_color(), - cmd.radius(), cmd.value()); - addCounter(c); - - Event_CreateCounter event; - ServerInfo_Counter *counterInfo = event.mutable_counter_info(); - counterInfo->set_id(c->getId()); - counterInfo->set_name(c->getName().toStdString()); - counterInfo->mutable_counter_color()->CopyFrom(cmd.counter_color()); - counterInfo->set_radius(c->getRadius()); - counterInfo->set_count(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_Counter *c = counters.value(cmd.counter_id(), 0); - if (!c) { - return Response::RespNameNotFound; - } - - c->setCount(cmd.value()); - - Event_SetCounter event; - event.set_counter_id(c->getId()); - event.set_value(c->getCount()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer & /*rc*/, GameEventStorage &ges) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - if (conceded) { - return Response::RespContextError; - } - - Server_Counter *counter = counters.value(cmd.counter_id(), 0); - if (!counter) { - return Response::RespNameNotFound; - } - counters.remove(cmd.counter_id()); - delete counter; - - Event_DelCounter event; - event.set_counter_id(cmd.counter_id()); - ges.enqueueGameEvent(event, playerId); - - return Response::RespOk; -} - -Response::ResponseCode -Server_Player::cmdNextTurn(const Command_NextTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage & /*ges*/) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - if (conceded && !judge) { - return Response::RespContextError; - } - - game->nextTurn(); - return Response::RespOk; -} - -Response::ResponseCode Server_Player::cmdSetActivePhase(const Command_SetActivePhase &cmd, - ResponseContainer & /*rc*/, - GameEventStorage & /*ges*/) -{ - if (!game->getGameStarted()) { - return Response::RespGameNotStarted; - } - - if (!judge) { - if (conceded) { - return Response::RespContextError; - } - - if (game->getActivePlayer() != playerId) { - return Response::RespContextError; - } - } - - game->setActivePhase(cmd.phase()); - - return Response::RespOk; -} - -Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, - ResponseContainer &rc, - GameEventStorage &ges) -{ - auto ret = Server_AbstractPlayer::cmdChangeZoneProperties(cmd, rc, ges); - - Server_CardZone *zone = zones.value(nameFromStdString(cmd.zone_name())); - if (!zone) { - return Response::RespNameNotFound; - } - - revealTopCardIfNeeded(zone, ges); - return ret; -} - -Response::ResponseCode -Server_Player::cmdReverseTurn(const Command_ReverseTurn &cmd, ResponseContainer &rc, GameEventStorage &ges) -{ - - if (!judge && conceded) { - return Response::RespContextError; - } - return Server_AbstractParticipant::cmdReverseTurn(cmd, rc, ges); -} - -void Server_Player::getInfo(ServerInfo_Player *info, - Server_AbstractParticipant *recipient, - bool omniscient, - bool withUserInfo) -{ - Server_AbstractPlayer::getInfo(info, recipient, omniscient, withUserInfo); - - for (Server_Counter *counter : counters) { - counter->getInfo(info->add_counter_list()); - } -} diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h deleted file mode 100644 index 5925ed3c2..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_player.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef PLAYER_H -#define PLAYER_H - -#include "server_abstract_player.h" - -class Server_Player : public Server_AbstractPlayer -{ - Q_OBJECT -private: - QMap counters; - QList lastDrawList; - -public: - Server_Player(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_handler); - ~Server_Player() override; - const QMap &getCounters() const - { - return counters; - } - int newCounterId() const; - void addCounter(Server_Counter *counter); - - void setupZones() override; - void clearZones() override; - - Response::ResponseCode drawCards(GameEventStorage &ges, int number); - void onCardBeingMoved(GameEventStorage &ges, - const MoveCardStruct &cardStruct, - Server_CardZone *startzone, - Server_CardZone *targetzone, - bool undoingDraw) override; - - Response::ResponseCode - cmdDeckSelect(const Command_DeckSelect &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdSetSideboardPlan(const Command_SetSideboardPlan &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdSetSideboardLock(const Command_SetSideboardLock &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdShuffle(const Command_Shuffle &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdMulligan(const Command_Mulligan &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdDrawCards(const Command_DrawCards &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdUndoDraw(const Command_UndoDraw &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdIncCounter(const Command_IncCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdCreateCounter(const Command_CreateCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdSetCounter(const Command_SetCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdDelCounter(const Command_DelCounter &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdNextTurn(const Command_NextTurn &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdSetActivePhase(const Command_SetActivePhase &cmd, ResponseContainer &rc, GameEventStorage &ges) override; - Response::ResponseCode - cmdReverseTurn(const Command_ReverseTurn & /*cmd*/, ResponseContainer & /*rc*/, GameEventStorage &ges) override; - Response::ResponseCode cmdChangeZoneProperties(const Command_ChangeZoneProperties &cmd, - ResponseContainer &rc, - GameEventStorage &ges) override; - - void getInfo(ServerInfo_Player *info, - Server_AbstractParticipant *playerWhosAsking, - bool omniscient, - bool withUserInfo) override; -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.cpp b/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.cpp deleted file mode 100644 index bc873e386..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "server_spectator.h" - -Server_Spectator::Server_Spectator(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_userInterface) - : Server_AbstractParticipant(_game, _playerId, _userInfo, _judge, _userInterface) -{ - spectator = true; -} diff --git a/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.h b/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.h deleted file mode 100644 index 4d3928a63..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/game/server_spectator.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SPECTATOR_H -#define SPECTATOR_H - -#include "server_abstract_participant.h" - -class Server_Spectator : public Server_AbstractParticipant -{ - Q_OBJECT -public: - Server_Spectator(Server_Game *_game, - int _playerId, - const ServerInfo_User &_userInfo, - bool _judge, - Server_AbstractUserInterface *_handler); -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h b/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h deleted file mode 100644 index b43dbde42..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef SERVER_DATABASE_INTERFACE_H -#define SERVER_DATABASE_INTERFACE_H - -#include "server.h" - -#include - -class Server_DatabaseInterface : public QObject -{ - Q_OBJECT -public: - explicit Server_DatabaseInterface(QObject *parent = nullptr) : QObject(parent) - { - } - - virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, - const QString &user, - const QString &password, - const QString &clientId, - QString &reasonStr, - int &secondsLeft, - bool passwordNeedsHash) = 0; - virtual bool checkUserIsBanned(const QString & /* ipAddress */, - const QString & /* userName */, - const QString & /* clientId */, - QString & /* banReason */, - int & /* banSecondsRemaining */) - { - return false; - } - virtual bool activeUserExists(const QString & /* user */) - { - return false; - } - virtual bool userExists(const QString & /* user */) - { - return false; - } - virtual QString getUserSalt(const QString & /* user */) - { - return {}; - } - virtual QMap getBuddyList(const QString & /* name */) - { - return QMap(); - } - virtual QMap getIgnoreList(const QString & /* name */) - { - return QMap(); - } - virtual bool isInBuddyList(const QString & /* whoseList */, const QString & /* who */) - { - return false; - } - virtual bool isInIgnoreList(const QString & /* whoseList */, const QString & /* who */) - { - return false; - } - virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; - virtual void storeGameInformation(const QString & /* roomName */, - const QStringList & /* roomGameTypes */, - const ServerInfo_Game & /* gameInfo */, - const QSet & /* allPlayersEver */, - const QSet & /* allSpectatorsEver */, - const QList & /* replayList */) - { - } - virtual DeckList *getDeckFromDatabase(int /* deckId */, int /* userId */) - { - return 0; - } - virtual bool removeForgotPassword(const QString & /* user */) - { - return false; - } - virtual qint64 startSession(const QString & /* userName */, - const QString & /* address */, - const QString & /* clientId */, - const QString & /* connectionType */) - { - return 0; - } - virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) - { - return true; - } -public slots: - virtual void endSession(qint64 /* sessionId */) - { - } - -public: - virtual int getNextGameId() = 0; - virtual int getNextReplayId() = 0; - virtual int getActiveUserCount(QString connectionType = QString()) = 0; - - virtual void clearSessionTables() - { - } - virtual void lockSessionTables() - { - } - virtual void unlockSessionTables() - { - } - virtual bool userSessionExists(const QString & /* userName */) - { - return false; - } - - virtual bool getRequireRegistration() - { - return false; - } - virtual bool registerUser(const QString & /* userName */, - const QString & /* realName */, - const QString & /* password */, - bool /* passwordNeedsHash */, - const QString & /* emailAddress */, - const QString & /* country */, - bool /* active = false */) - { - return false; - } - virtual bool activateUser(const QString & /* userName */, const QString & /* token */) - { - return false; - } - virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */) - { - } - virtual void updateUsersLastLoginData(const QString & /* userName */, const QString & /* clientVersion */) - { - } - - enum LogMessage_TargetType - { - MessageTargetRoom, - MessageTargetGame, - MessageTargetChat, - MessageTargetIslRoom - }; - virtual void logMessage(const int /* senderId */, - const QString & /* senderName */, - const QString & /* senderIp */, - const QString & /* logMessage */, - LogMessage_TargetType /* targetType */, - const int /* targetId */, - const QString & /* targetName */) - { - } - virtual bool checkUserIsBanned(Server_ProtocolHandler * /* session */, - QString & /* banReason */, - int & /* banSecondsRemaining */) - { - return false; - } - virtual int checkNumberOfUserAccounts(const QString & /* email */) - { - return 0; - } - virtual bool - changeUserPassword(const QString & /* user */, const QString & /* password */, bool /* passwordNeedsHash */) - { - return false; - } - virtual bool changeUserPassword(const QString & /* user */, - const QString & /* oldPassword */, - bool /* oldPasswordNeedsHash */, - const QString & /* newPassword */, - bool /* newPasswordNeedsHash */) - { - return false; - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_player_reference.h b/libcockatrice_network/libcockatrice/network/server/remote/server_player_reference.h deleted file mode 100644 index 07b2d3d2b..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_player_reference.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef SERVER_PLAYER_REFERENCE_H -#define SERVER_PLAYER_REFERENCE_H - -class PlayerReference -{ -private: - int roomId; - int gameId; - int playerId; - -public: - PlayerReference(int _roomId, int _gameId, int _playerId) : roomId(_roomId), gameId(_gameId), playerId(_playerId) - { - } - int getRoomId() const - { - return roomId; - } - int getGameId() const - { - return gameId; - } - int getPlayerId() const - { - return playerId; - } - bool operator==(const PlayerReference &other) - { - return ((roomId == other.roomId) && (gameId == other.gameId) && (playerId == other.playerId)); - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_response_containers.h b/libcockatrice_network/libcockatrice/network/server/remote/server_response_containers.h deleted file mode 100644 index 118b32d38..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_response_containers.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef SERVER_RESPONSE_CONTAINERS_H -#define SERVER_RESPONSE_CONTAINERS_H - -#include -#include -#include - -namespace google -{ -namespace protobuf -{ -class Message; -} -} // namespace google -class Server_Game; - -class GameEventStorageItem -{ -public: - enum EventRecipient - { - SendToPrivate = 0x01, - SendToOthers = 0x02 - }; - Q_DECLARE_FLAGS(EventRecipients, EventRecipient) -private: - GameEvent *event; - EventRecipients recipients; - -public: - GameEventStorageItem(const ::google::protobuf::Message &_event, int _playerId, EventRecipients _recipients); - ~GameEventStorageItem(); - - [[nodiscard]] const GameEvent &getGameEvent() const - { - return *event; - } - [[nodiscard]] EventRecipients getRecipients() const - { - return recipients; - } -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(GameEventStorageItem::EventRecipients) - -class GameEventStorage -{ -private: - ::google::protobuf::Message *gameEventContext; - QList gameEventList; - int privatePlayerId; - int forcedByJudge = -1; - bool overwriteOwnership = false; - -public: - GameEventStorage(); - ~GameEventStorage(); - - void setGameEventContext(const ::google::protobuf::Message &_gameEventContext); - [[nodiscard]] ::google::protobuf::Message *getGameEventContext() const - { - return gameEventContext; - } - [[nodiscard]] const QList &getGameEventList() const - { - return gameEventList; - } - [[nodiscard]] int getPrivatePlayerId() const - { - return privatePlayerId; - } - void setForcedByJudge(int playerId) - { - forcedByJudge = playerId; - } - void setOverwriteOwnership(bool shouldOverwriteOwnership) - { - overwriteOwnership = shouldOverwriteOwnership; - } - - void enqueueGameEvent(const ::google::protobuf::Message &event, - int playerId, - GameEventStorageItem::EventRecipients recipients = GameEventStorageItem::SendToPrivate | - GameEventStorageItem::SendToOthers, - int _privatePlayerId = -1); - void sendToGame(Server_Game *game); -}; - -class ResponseContainer -{ -private: - int cmdId; - ::google::protobuf::Message *responseExtension; - QList> preResponseQueue, postResponseQueue; - -public: - ResponseContainer(int _cmdId); - ~ResponseContainer(); - - [[nodiscard]] int getCmdId() const - { - return cmdId; - } - void setResponseExtension(::google::protobuf::Message *_responseExtension) - { - responseExtension = _responseExtension; - } - [[nodiscard]] ::google::protobuf::Message *getResponseExtension() const - { - return responseExtension; - } - void enqueuePreResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) - { - preResponseQueue.append(qMakePair(type, item)); - } - void enqueuePostResponseItem(ServerMessage::MessageType type, ::google::protobuf::Message *item) - { - postResponseQueue.append(qMakePair(type, item)); - } - [[nodiscard]] const QList> & - getPreResponseQueue() const - { - return preResponseQueue; - } - [[nodiscard]] const QList> & - getPostResponseQueue() const - { - return postResponseQueue; - } -}; - -#endif diff --git a/libcockatrice_network/libcockatrice/network/server/remote/serverinfo_user_container.h b/libcockatrice_network/libcockatrice/network/server/remote/serverinfo_user_container.h deleted file mode 100644 index a959f4535..000000000 --- a/libcockatrice_network/libcockatrice/network/server/remote/serverinfo_user_container.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SERVERINFO_USER_CONTAINER -#define SERVERINFO_USER_CONTAINER - -class ServerInfo_User; - -class ServerInfo_User_Container -{ -protected: - ServerInfo_User *userInfo; - -public: - explicit ServerInfo_User_Container(ServerInfo_User *_userInfo = nullptr); - explicit ServerInfo_User_Container(const ServerInfo_User &_userInfo); - ServerInfo_User_Container(const ServerInfo_User_Container &other); - ServerInfo_User_Container &operator=(const ServerInfo_User_Container &other) = default; - virtual ~ServerInfo_User_Container(); - [[nodiscard]] ServerInfo_User *getUserInfo() const - { - return userInfo; - } - void setUserInfo(const ServerInfo_User &_userInfo); - ServerInfo_User & - copyUserInfo(ServerInfo_User &result, bool complete, bool internalInfo = false, bool sessionInfo = false) const; - [[nodiscard]] ServerInfo_User - copyUserInfo(bool complete, bool internalInfo = false, bool sessionInfo = false) const; -}; - -#endif diff --git a/libcockatrice_protocol/CMakeLists.txt b/libcockatrice_protocol/CMakeLists.txt deleted file mode 100644 index 7dc0a0360..000000000 --- a/libcockatrice_protocol/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# Top-level wrapper for the protobuf library - -add_subdirectory(libcockatrice/protocol/pb) - -add_library(libcockatrice_protocol STATIC) - -set(SOURCES libcockatrice/protocol/debug_pb_message.cpp libcockatrice/protocol/featureset.cpp - libcockatrice/protocol/get_pb_extension.cpp libcockatrice/protocol/pending_command.cpp -) - -set(HEADERS libcockatrice/protocol/debug_pb_message.h libcockatrice/protocol/featureset.h - libcockatrice/protocol/get_pb_extension.h libcockatrice/protocol/pending_command.h -) - -target_sources(libcockatrice_protocol PRIVATE ${SOURCES} ${HEADERS}) - -add_dependencies(libcockatrice_protocol libcockatrice_protocol_pb) - -# Link the actual generated protobuf library -target_link_libraries(libcockatrice_protocol PUBLIC ${QT_CORE_MODULE} libcockatrice_protocol_pb libcockatrice_utility) - -# Expose include paths -target_include_directories( - libcockatrice_protocol PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} # points to the generated headers -) diff --git a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp b/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp deleted file mode 100644 index 718487c18..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "debug_pb_message.h" - -#include -#include -#include -#include -#include -#include - -// FastFieldValuePrinter is added in protobuf 3.4, going out of our way to add the old FieldValuePrinter is not worth it -#if GOOGLE_PROTOBUF_VERSION > 3004000 - -// value printer to use for all values, will snip too long contents -class LimitedPrinter : public ::google::protobuf::TextFormat::FastFieldValuePrinter -{ -public: - void PrintString(const std::string &val, - ::google::protobuf::TextFormat::BaseTextGenerator *generator) const override; -}; - -// value printer to use for specifc values, will expunge sensitive info -class SafePrinter : public ::google::protobuf::TextFormat::FastFieldValuePrinter -{ -public: - void PrintString(const std::string &val, - ::google::protobuf::TextFormat::BaseTextGenerator *generator) const override; - - static void applySafePrinter(const ::google::protobuf::Message &message, - ::google::protobuf::TextFormat::Printer &printer); -}; - -void LimitedPrinter::PrintString(const std::string &val, - ::google::protobuf::TextFormat::BaseTextGenerator *generator) const -{ - auto length = val.length(); - if (length > MAX_TEXT_LENGTH) { - ::google::protobuf::TextFormat::FastFieldValuePrinter::PrintString( - val.substr(0, MAX_NAME_LENGTH) + "... ---snip--- (" + std::to_string(length) + " bytes total", generator); - } else { - ::google::protobuf::TextFormat::FastFieldValuePrinter::PrintString(val, generator); - } -} - -void SafePrinter::PrintString(const std::string & /*val*/, - ::google::protobuf::TextFormat::BaseTextGenerator *generator) const -{ - generator->PrintLiteral("\" ---value expunged--- \""); -} - -void SafePrinter::applySafePrinter(const ::google::protobuf::Message &message, - ::google::protobuf::TextFormat::Printer &printer) -{ - const auto *reflection = message.GetReflection(); - std::vector fields; - reflection->ListFields(message, &fields); - for (const auto *field : fields) { - switch (field->cpp_type()) { - case ::google::protobuf::FieldDescriptor::CPPTYPE_STRING: - if (field->name().find("password") != std::string::npos) { // name contains password - auto *safePrinter = new SafePrinter(); - if (!printer.RegisterFieldValuePrinter(field, safePrinter)) - delete safePrinter; // in case safePrinter has not been taken ownership of - } - break; - case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: - if (field->is_repeated()) { - for (int i = 0; i < reflection->FieldSize(message, field); ++i) { - applySafePrinter(reflection->GetRepeatedMessage(message, field, i), printer); - } - } else { - applySafePrinter(reflection->GetMessage(message, field), printer); - } - break; - default: - break; - } - } -} -#endif // GOOGLE_PROTOBUF_VERSION > 3004000 - -QString getSafeDebugString(const ::google::protobuf::Message &message) -{ -#if GOOGLE_PROTOBUF_VERSION > 3001000 - auto size = message.ByteSizeLong(); -#else - auto size = message.ByteSize(); -#endif - - ::google::protobuf::TextFormat::Printer printer; - printer.SetSingleLineMode(true); // compact mode - printer.SetExpandAny(true); // prints all fields - -#if GOOGLE_PROTOBUF_VERSION > 3004000 - // printer takes ownership of the LimitedPrinter and will delete it - printer.SetDefaultFieldValuePrinter(new LimitedPrinter()); - // check field names an create SafePrinters for necessary fields - SafePrinter::applySafePrinter(message, printer); -#else - // removing passwords from debug output will only be supported on newer protobuf versions - printer.SetTruncateStringFieldLongerThan(MAX_TEXT_LENGTH); -#endif // GOOGLE_PROTOBUF_VERSION > 3004000 - - std::string debug_string; - bool ok = printer.PrintToString(message, &debug_string); - if (ok) { - return QString::number(size) + " bytes " + QString::fromStdString(debug_string); - } else { - return "[could not convert message to string]"; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.h b/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.h deleted file mode 100644 index 34e7845c5..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/debug_pb_message.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef DEBUG_PB_MESSAGE_H -#define DEBUG_PB_MESSAGE_H - -class QString; -namespace google -{ -namespace protobuf -{ -class Message; -} -} // namespace google - -QString getSafeDebugString(const ::google::protobuf::Message &message); - -#endif // DEBUG_PB_MESSAGE_H diff --git a/libcockatrice_protocol/libcockatrice/protocol/featureset.cpp b/libcockatrice_protocol/libcockatrice/protocol/featureset.cpp deleted file mode 100644 index 1b08c4040..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/featureset.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "featureset.h" - -#include - -FeatureSet::FeatureSet() -{ -} - -QMap FeatureSet::getDefaultFeatureList() -{ - initalizeFeatureList(featureList); - return featureList; -} - -void FeatureSet::initalizeFeatureList(QMap &_featureList) -{ - // default features [name], [is required to connect] - _featureList.insert("client_id", false); - _featureList.insert("client_ver", false); - _featureList.insert("feature_set", false); - _featureList.insert("user_ban_history", false); - _featureList.insert("room_chat_history", false); - _featureList.insert("client_warnings", false); - _featureList.insert("mod_log_lookup", false); - _featureList.insert("idle_client", false); - _featureList.insert("forgot_password", false); - _featureList.insert("websocket", false); - // featureList.insert("hashed_password_login", false); - // These are temp to force users onto a newer client - _featureList.insert("2.7.0_min_version", false); - _featureList.insert("2.8.0_min_version", false); -} - -void FeatureSet::enableRequiredFeature(QMap &_featureList, const QString &featureName) -{ - if (_featureList.contains(featureName)) - _featureList.insert(featureName, true); -} - -void FeatureSet::disableRequiredFeature(QMap &_featureList, const QString &featureName) -{ - if (_featureList.contains(featureName)) - _featureList.insert(featureName, false); -} - -QMap -FeatureSet::addFeature(QMap &_featureList, const QString &featureName, bool isFeatureRequired) -{ - _featureList.insert(featureName, isFeatureRequired); - return _featureList; -} - -QMap FeatureSet::identifyMissingFeatures(const QMap &suppliedFeatures, - QMap requiredFeatures) -{ - QMap missingList; - QMap::iterator i; - for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) { - if (!suppliedFeatures.contains(i.key())) { - missingList.insert(i.key(), i.value()); - } - } - return missingList; -} - -bool FeatureSet::isRequiredFeaturesMissing(const QMap &suppliedFeatures, - QMap requiredFeatures) -{ - QMap::iterator i; - for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) { - if (i.value() && suppliedFeatures.contains(i.key())) { - return true; - } - } - return false; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/featureset.h b/libcockatrice_protocol/libcockatrice/protocol/featureset.h deleted file mode 100644 index 32625d5f8..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/featureset.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef FEATURESET_H -#define FEATURESET_H - -#include -#include -#include - -class FeatureSet : public QObject -{ -public: - FeatureSet(); - QMap getDefaultFeatureList(); - void initalizeFeatureList(QMap &_featureList); - void enableRequiredFeature(QMap &_featureList, const QString &featureName); - void disableRequiredFeature(QMap &_featureList, const QString &featureName); - QMap - addFeature(QMap &_featureList, const QString &featureName, bool isFeatureRequired); - QMap identifyMissingFeatures(const QMap &featureListToCheck, - QMap featureListToCompareTo); - bool isRequiredFeaturesMissing(const QMap &featureListToCheck, - QMap featureListToCompareTo); - -private: - QMap featureList; -}; - -#endif // FEEATURESET_H diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/color.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/color.proto deleted file mode 100644 index 3daae6ce2..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/color.proto +++ /dev/null @@ -1,16 +0,0 @@ -syntax = "proto2"; - -// Container for a 4 component color code -message color { - // the red component of the color, limited to 256 values - optional uint32 r = 1; - - // the green component of the color, limited to 256 values - optional uint32 g = 2; - - // the blue component of the color, limited to 256 values - optional uint32 b = 3; - - // the opacity component of the color, limited to 256 values - optional uint32 a = 4; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_arrow.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_arrow.proto deleted file mode 100644 index cb3871b60..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_create_arrow.proto +++ /dev/null @@ -1,35 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; -import "color.proto"; - -// Command to draw an arrow from cards to either other cards or a player -message Command_CreateArrow { - extend GameCommand { - optional Command_CreateArrow ext = 1011; - } - // the player that has the card the arrow is drawn from - optional sint32 start_player_id = 1 [default = -1]; - - // the zone that the card the arrow is drawn from is in - optional string start_zone = 2; - - // the id of the card that the arrow is drawn from - optional sint32 start_card_id = 3 [default = -1]; - - // the player that has the card the arrow is drawn to, or that the arrow is drawn to if not a card - optional sint32 target_player_id = 4 [default = -1]; - - // the zone that the card the arrow is drawn to is in, the player will be targeted if this is absent - optional string target_zone = 5; - - // the id of the card that the arrow is drawn to, the player will be targeted if this is absent - optional sint32 target_card_id = 6 [default = -1]; - - // the color of the arrow - optional color arrow_color = 7; - - // the phase that this arrow is deleted in, arrows are deleted when this or a later phase is activated and also - // when the phase moves back before the current phase or the turn is passed, when not set the arrow is deleted - // immediately when the phase is changed - optional sint32 delete_in_phase = 8; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto deleted file mode 100644 index a5b96da2e..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_move_card.proto +++ /dev/null @@ -1,55 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; - -// Container describing a single card to move -message CardToMove { - // Id of the card in its current zone - optional sint32 card_id = 1 [default = -1]; - - // If true, places the card face down, hiding its name. - // If false, forcibly turns the card face up. - // If not set, defers the resulting face down state to the server. - optional bool face_down = 2; - - // When moving add this value to the power/toughness field of the card - optional string pt = 3; - - // When moving sets the card to be tapped - optional bool tapped = 4; -} - -// Container of multiple cards to move -message ListOfCardsToMove { - repeated CardToMove card = 1; -} - -// Command to move an amount of cards from one zone to another index/coordinate or another zone -message Command_MoveCard { - extend GameCommand { - optional Command_MoveCard ext = 1027; - } - - // The player the zone the cards are in belongs to - optional sint32 start_player_id = 1 [default = -1]; - - // The zone the cards start in - optional string start_zone = 2; - - // List of the cards and their new properties - optional ListOfCardsToMove cards_to_move = 3; - - // The player the zone the cards will be moved to belongs to - optional sint32 target_player_id = 4 [default = -1]; - - // The zone the cards will be moved to - optional string target_zone = 5; - - // New x coordinate of the first card in the list - optional sint32 x = 6 [default = -1]; - - // New y coordinate of the first card in the list - optional sint32 y = 7 [default = -1]; - - // Inverts the x coordinate to apply from the end of the target zone instead of the start - optional bool is_reversed = 8 [default = false]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_get_code.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_get_code.proto deleted file mode 100644 index e4573153c..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_get_code.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_ReplayGetCode { - extend SessionCommand { - optional Command_ReplayGetCode ext = 1104; - } - optional sint32 game_id = 1 [default = -1]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_submit_code.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_submit_code.proto deleted file mode 100644 index 73c5d0ba0..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_replay_submit_code.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "session_commands.proto"; - -message Command_ReplaySubmitCode { - extend SessionCommand { - optional Command_ReplaySubmitCode ext = 1105; - } - optional string replay_code = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_reverse_turn.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_reverse_turn.proto deleted file mode 100644 index c5cc1c4d9..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_reverse_turn.proto +++ /dev/null @@ -1,7 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; -message Command_ReverseTurn { - extend GameCommand { - optional Command_ReverseTurn ext = 1034; - } -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/command_shuffle.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/command_shuffle.proto deleted file mode 100644 index a99f90896..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/command_shuffle.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "game_commands.proto"; -message Command_Shuffle { - extend GameCommand { - optional Command_Shuffle ext = 1003; - } - optional string zone_name = 1; - optional sint32 start = 2 [default = 0]; - optional sint32 end = 3 [default = -1]; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_state_changed.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_state_changed.proto deleted file mode 100644 index 5c8aee3fe..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_game_state_changed.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto2"; -import "game_event.proto"; -import "serverinfo_player.proto"; - -// Signals that the game state has changed. -// If a field is present in this message, it will overwrite the client's game state. -// Also used to provide the entire game state when joining a game. -message Event_GameStateChanged { - extend GameEvent { - optional Event_GameStateChanged ext = 1005; - } - - // the list of players. Players contain their zones which contain all cards in the game - repeated ServerInfo_Player player_list = 1; - - // if the game has started - optional bool game_started = 2; - - // the player who is currently holding the turn - optional sint32 active_player_id = 3; - - // the current phase - optional sint32 active_phase = 4; - - // the amount of seconds since the game started - optional uint32 seconds_elapsed = 5; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_leave.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_leave.proto deleted file mode 100644 index 6e1ed871d..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_leave.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto2"; -import "game_event.proto"; - -message Event_Leave { - extend GameEvent { - optional Event_Leave ext = 1001; - } - enum LeaveReason { - OTHER = 1; - USER_KICKED = 2; - USER_LEFT = 3; - USER_DISCONNECTED = 4; - } - optional LeaveReason reason = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_move_card.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_move_card.proto deleted file mode 100644 index e8e60066a..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_move_card.proto +++ /dev/null @@ -1,48 +0,0 @@ -syntax = "proto2"; -import "game_event.proto"; - -// Sent by the server to signal a single card was moved to update the client state -message Event_MoveCard { - extend GameEvent { - optional Event_MoveCard ext = 2009; - } - - // The card id in the original zone - optional sint32 card_id = 1 [default = -1]; - - // The name of the card in case it was not known yet - optional string card_name = 2; - - // The player whose zone the card started in - optional sint32 start_player_id = 3 [default = -1]; - - // The original zone - optional string start_zone = 4; - - // The original position that the card was at. - // In zones without y coordinate, this corresponds with the previous x coordinate of the card. - // In zones with y coordinate, this value is not used. - optional sint32 position = 5 [default = -1]; - - // The player who owns the new zone the card is in - optional sint32 target_player_id = 6 [default = -1]; - - // The new zone the card is in - optional string target_zone = 7; - - // The new x coordinate (or new position for zones with no y coordinate) - optional sint32 x = 8 [default = -1]; - - // The new y coordinate - optional sint32 y = 9 [default = -1]; - - // The new id of the card if the card moved zone - optional sint32 new_card_id = 10 [default = -1]; - - // If the card is face down, face down cards will not show their name - optional bool face_down = 11; - - // The provider id of the card in case it was not known yet. - // Extends the name to supply a specific printing of that type of card. - optional string new_card_provider_id = 12; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_remove_messages.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_remove_messages.proto deleted file mode 100644 index bb1023464..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_remove_messages.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "room_event.proto"; - -message Event_RemoveMessages { - extend RoomEvent { - optional Event_RemoveMessages ext = 1004; - } - optional string name = 1; - optional uint32 amount = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/event_reverse_turn.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/event_reverse_turn.proto deleted file mode 100644 index 0357d5c0a..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/event_reverse_turn.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "game_event.proto"; - -message Event_ReverseTurn { - extend GameEvent { - optional Event_ReverseTurn ext = 2021; - } - optional bool reversed = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto deleted file mode 100644 index 9d01b51d2..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/moderator_commands.proto +++ /dev/null @@ -1,108 +0,0 @@ -syntax = "proto2"; -message ModeratorCommand { - enum ModeratorCommandType { - BAN_FROM_SERVER = 1000; - BAN_HISTORY = 1001; - WARN_USER = 1002; - WARN_HISTORY = 1003; - WARN_LIST = 1004; - VIEWLOG_HISTORY = 1005; - GRANT_REPLAY_ACCESS = 1006; - FORCE_ACTIVATE_USER = 1007; - GET_ADMIN_NOTES = 1008; - UPDATE_ADMIN_NOTES = 1009; - } - extensions 100 to max; -} - -message Command_BanFromServer { - extend ModeratorCommand { - optional Command_BanFromServer ext = 1000; - } - optional string user_name = 1; - optional string address = 2; - optional uint32 minutes = 3; - optional string reason = 4; - optional string visible_reason = 5; - optional string clientid = 6; - optional uint32 remove_messages = 7; -} - -message Command_GetBanHistory { - extend ModeratorCommand { - optional Command_GetBanHistory ext = 1001; - } - optional string user_name = 1; -} - -message Command_WarnUser { - extend ModeratorCommand { - optional Command_WarnUser ext = 1002; - } - - optional string user_name = 1; - optional string reason = 2; - optional string clientid = 3; - optional uint32 remove_messages = 4; -} - -message Command_GetWarnHistory { - extend ModeratorCommand { - optional Command_GetWarnHistory ext = 1003; - } - optional string user_name = 1; -} - -message Command_GetWarnList { - extend ModeratorCommand { - optional Command_GetWarnList ext = 1004; - } - optional string mod_name = 1; - optional string user_name = 2; - optional string user_clientid = 3; -} - -message Command_ViewLogHistory { - extend ModeratorCommand { - optional Command_ViewLogHistory ext = 1005; - } - optional string user_name = 1; // user that created message - optional string ip_address = 2; // ip address of user that created message - optional string game_name = 3; // client id of user that created the message - optional string game_id = 4; // game number the message was sent to - optional string message = 5; // raw message that was sent - repeated string log_location = 6; // destination of message (ex: main room, game room, private chat) - required uint32 date_range = 7; // the length of time (in minutes) to look back for - optional uint32 maximum_results = 8; // the maximum number of query results -} - -message Command_GrantReplayAccess { - extend ModeratorCommand { - optional Command_GrantReplayAccess ext = 1006; - } - optional uint32 replay_id = 1; - optional string moderator_name = 2; -} - -message Command_ForceActivateUser { - extend ModeratorCommand { - optional Command_ForceActivateUser ext = 1007; - } - optional string username_to_activate = 1; - optional string moderator_name = 2; -} - -message Command_GetAdminNotes { - extend ModeratorCommand { - optional Command_GetAdminNotes ext = 1008; - } - optional string user_name = 1; -} - -message Command_UpdateAdminNotes { - extend ModeratorCommand { - optional Command_UpdateAdminNotes ext = 1009; - } - optional string user_name = 1; - optional string notes = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_forgotpasswordrequest.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_forgotpasswordrequest.proto deleted file mode 100644 index 5205e0fdc..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_forgotpasswordrequest.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_ForgotPasswordRequest { - extend Response { - optional Response_ForgotPasswordRequest ext = 1016; - } - optional bool challenge_email = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_admin_notes.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_admin_notes.proto deleted file mode 100644 index f08bce5d5..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_get_admin_notes.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_GetAdminNotes { - extend Response { - optional Response_GetAdminNotes ext = 1018; - } - optional string user_name = 1; - optional string notes = 2; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_password_salt.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_password_salt.proto deleted file mode 100644 index 3fc228530..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_password_salt.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_PasswordSalt { - extend Response { - optional Response_PasswordSalt ext = 1017; - } - optional string password_salt = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_get_code.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_get_code.proto deleted file mode 100644 index 5bb1b511a..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/response_replay_get_code.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = "proto2"; -import "response.proto"; - -message Response_ReplayGetCode { - extend Response { - optional Response_ReplayGetCode ext = 1102; - } - optional string replay_code = 1; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_ban.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_ban.proto deleted file mode 100644 index 241fc3b50..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_ban.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto2"; -/* - * Historical ban information stored in the ban table - */ -message ServerInfo_Ban { - required string admin_id = 1; // id of the staff member placing the ban - required string admin_name = 2; // name of the staff member placing the ban - required string ban_time = 3; // start time of the ban - required string ban_length = 4; // amount of time in minutes the ban is for - optional string ban_reason = 5; // reason seen only by moderation staff - optional string visible_reason = 6; // reason shown to the user -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_card.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_card.proto deleted file mode 100644 index a9a8b5c9d..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_card.proto +++ /dev/null @@ -1,56 +0,0 @@ -syntax = "proto2"; -import "serverinfo_cardcounter.proto"; - -// Container for all the properties of a single card -message ServerInfo_Card { - // unique card id in this zone - optional sint32 id = 1 [default = -1]; - - // name of this kind of card - optional string name = 2; - - // x coordinate in zone - optional sint32 x = 3 [default = -1]; - - // y coordinate in zone - optional sint32 y = 4 [default = -1]; - - // if the card is face_down, hiding its information - optional bool face_down = 5; - - // if the card is tapped, turned sideways - optional bool tapped = 6; - - // if the card is marked as attacking - optional bool attacking = 7; - - // the card's color - optional string color = 8; - - // the power/toughness field, displayed on the bottom right - optional string pt = 9; - - // an optional string placed over the card - optional string annotation = 10; - - // whether the card should be deleted when removed from the table, like a token - optional bool destroy_on_zone_change = 11; - - // whether the card should not be untapped when the untap command is sent - optional bool doesnt_untap = 12; - - // list of counters on the card - repeated ServerInfo_CardCounter counter_list = 13; - - // the player that owns the card this is attached to - optional sint32 attach_player_id = 14 [default = -1]; - - // the zone of the card this is attached to - optional string attach_zone = 15; - - // the unique id of the card in that zone - optional sint32 attach_card_id = 16 [default = -1]; - - // unique id of this kind of card, extends the name to specify a specific printing of a card - optional string provider_id = 17; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_game.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_game.proto deleted file mode 100644 index 9a56e034c..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_game.proto +++ /dev/null @@ -1,65 +0,0 @@ -syntax = "proto2"; -import "serverinfo_user.proto"; - -// Container for information about a game in the room's game list -message ServerInfo_Game { - // id of server the game is on - optional sint32 server_id = 1 [default = -1]; - - // id of room the game is in - optional sint32 room_id = 2 [default = -1]; - - // unique id of the game inside the room - optional sint32 game_id = 3 [default = -1]; - - // user provided game description - optional string description = 4; - - // password required to join game - optional bool with_password = 5; - - // players required to play - optional uint32 max_players = 6; - - // mask of server defined game types - repeated sint32 game_types = 7; - - // user that created the game - optional ServerInfo_User creator_info = 8; - - // only buddies of the creator can join this game - optional bool only_buddies = 9; - - // only registered users can join this game - optional bool only_registered = 10; - - // if spectators are allowed to join - optional bool spectators_allowed = 11; - - // spectators need to enter the game - optional bool spectators_need_password = 12; - - // spectators can use cmdGameSay - optional bool spectators_can_chat = 13; - - // spectators receive private events for all players - optional bool spectators_omniscient = 14; - - // decklists are sent to all players when loaded - optional bool share_decklists_on_load = 15; - - // the current player count - optional uint32 player_count = 30; - - // the current spectator count - optional uint32 spectators_count = 31; - - // whether the game is currently ongoing - optional bool started = 50; - - // time that the game started at - optional uint32 start_time = 51; - - // whether the game is closed. Closed games are finished and can't be interacted with - optional bool closed = 52; -} diff --git a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_warning.proto b/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_warning.proto deleted file mode 100644 index 20287be06..000000000 --- a/libcockatrice_protocol/libcockatrice/protocol/pb/serverinfo_warning.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto2"; -/* - * Historical warning information stored in the warnings table - */ -message ServerInfo_Warning { - optional string user_name = 1; // name of user being warned - optional string admin_name = 2; // name of the moderator making the warning - optional string reason = 3; // type of warning being placed - optional string time_of = 4; // time of warning -} diff --git a/libcockatrice_rng/CMakeLists.txt b/libcockatrice_rng/CMakeLists.txt deleted file mode 100644 index 6ff2a4537..000000000 --- a/libcockatrice_rng/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS libcockatrice/rng/rng_abstract.h libcockatrice/rng/rng_sfmt.h libcockatrice/rng/sfmt/SFMT.h) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_rng STATIC ${MOC_SOURCES} libcockatrice/rng/rng_abstract.cpp libcockatrice/rng/rng_sfmt.cpp - libcockatrice/rng/sfmt/SFMT.c -) - -target_include_directories(libcockatrice_rng PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_rng PUBLIC ${QT_CORE_MODULE}) diff --git a/libcockatrice_settings/CMakeLists.txt b/libcockatrice_settings/CMakeLists.txt deleted file mode 100644 index 3afe6e00a..000000000 --- a/libcockatrice_settings/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(HEADERS - libcockatrice/settings/card_database_settings.h - libcockatrice/settings/card_override_settings.h - libcockatrice/settings/debug_settings.h - libcockatrice/settings/download_settings.h - libcockatrice/settings/game_filters_settings.h - libcockatrice/settings/layouts_settings.h - libcockatrice/settings/message_settings.h - libcockatrice/settings/recents_settings.h - libcockatrice/settings/servers_settings.h - libcockatrice/settings/settings_manager.h -) - -if(Qt6_FOUND) - qt6_wrap_cpp(MOC_SOURCES ${HEADERS}) -elseif(Qt5_FOUND) - qt5_wrap_cpp(MOC_SOURCES ${HEADERS}) -endif() - -add_library( - libcockatrice_settings STATIC - ${MOC_SOURCES} - libcockatrice/settings/card_database_settings.cpp - libcockatrice/settings/card_override_settings.cpp - libcockatrice/settings/debug_settings.cpp - libcockatrice/settings/download_settings.cpp - libcockatrice/settings/game_filters_settings.cpp - libcockatrice/settings/layouts_settings.cpp - libcockatrice/settings/message_settings.cpp - libcockatrice/settings/recents_settings.cpp - libcockatrice/settings/servers_settings.cpp - libcockatrice/settings/settings_manager.cpp -) - -target_include_directories( - libcockatrice_settings - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - PUBLIC ${CMAKE_SOURCE_DIR}/cockatrice/src/client/network -) - -target_link_libraries(libcockatrice_settings PUBLIC libcockatrice_card libcockatrice_utility ${QT_CORE_MODULE}) diff --git a/libcockatrice_settings/libcockatrice/settings/card_database_settings.cpp b/libcockatrice_settings/libcockatrice/settings/card_database_settings.cpp deleted file mode 100644 index 26a91a4dd..000000000 --- a/libcockatrice_settings/libcockatrice/settings/card_database_settings.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "card_database_settings.h" - -CardDatabaseSettings::CardDatabaseSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "cardDatabase.ini", QString(), QString(), parent) -{ -} - -void CardDatabaseSettings::setSortKey(QString shortName, unsigned int sortKey) -{ - setValue(sortKey, "sortkey", "sets", std::move(shortName)); -} - -void CardDatabaseSettings::setEnabled(QString shortName, bool enabled) -{ - setValue(enabled, "enabled", "sets", std::move(shortName)); -} - -void CardDatabaseSettings::setIsKnown(QString shortName, bool isknown) -{ - setValue(isknown, "isknown", "sets", std::move(shortName)); -} - -unsigned int CardDatabaseSettings::getSortKey(QString shortName) const -{ - return getValue("sortkey", "sets", std::move(shortName)).toUInt(); -} - -bool CardDatabaseSettings::isEnabled(QString shortName) const -{ - return getValue("enabled", "sets", std::move(shortName)).toBool(); -} - -bool CardDatabaseSettings::isKnown(QString shortName) const -{ - return getValue("isknown", "sets", std::move(shortName)).toBool(); -} diff --git a/libcockatrice_settings/libcockatrice/settings/card_database_settings.h b/libcockatrice_settings/libcockatrice/settings/card_database_settings.h deleted file mode 100644 index bb946ea80..000000000 --- a/libcockatrice_settings/libcockatrice/settings/card_database_settings.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @file card_database_settings.h - * @ingroup CardDatabase - * @ingroup CardSettings - * @brief TODO: Document this. - */ - -#ifndef CARDDATABASESETTINGS_H -#define CARDDATABASESETTINGS_H - -#include "settings_manager.h" - -#include - -class CardDatabaseSettings : public SettingsManager, public ICardSetPriorityController -{ - Q_OBJECT - friend class SettingsCache; - -public: - void setSortKey(QString shortName, unsigned int sortKey) override; - void setEnabled(QString shortName, bool enabled) override; - void setIsKnown(QString shortName, bool isknown) override; - - unsigned int getSortKey(QString shortName) const override; - bool isEnabled(QString shortName) const override; - bool isKnown(QString shortName) const override; - -private: - explicit CardDatabaseSettings(const QString &settingPath, QObject *parent = nullptr); -}; - -#endif // CARDDATABASESETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/card_override_settings.cpp b/libcockatrice_settings/libcockatrice/settings/card_override_settings.cpp deleted file mode 100644 index a61a4693b..000000000 --- a/libcockatrice_settings/libcockatrice/settings/card_override_settings.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "card_override_settings.h" - -CardOverrideSettings::CardOverrideSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "cardPreferenceOverrides.ini", "cards", QString(), parent) -{ -} - -void CardOverrideSettings::setCardPreferenceOverride(const CardRef &cardRef) -{ - setValue(cardRef.providerId, cardRef.name); -} - -void CardOverrideSettings::deleteCardPreferenceOverride(const QString &cardName) -{ - deleteValue(cardName); -} - -QString CardOverrideSettings::getCardPreferenceOverride(const QString &cardName) const -{ - return getValue(cardName).toString(); -} \ No newline at end of file diff --git a/libcockatrice_settings/libcockatrice/settings/card_override_settings.h b/libcockatrice_settings/libcockatrice/settings/card_override_settings.h deleted file mode 100644 index 3d9db4e65..000000000 --- a/libcockatrice_settings/libcockatrice/settings/card_override_settings.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file card_override_settings.h - * @ingroup CardSettings - * @brief TODO: Document this. - */ - -#ifndef COCKATRICE_CARD_OVERRIDE_SETTINGS_H -#define COCKATRICE_CARD_OVERRIDE_SETTINGS_H - -#include "settings_manager.h" - -#include -#include - -class CardOverrideSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - -public: - void setCardPreferenceOverride(const CardRef &cardRef); - - void deleteCardPreferenceOverride(const QString &cardName); - - QString getCardPreferenceOverride(const QString &cardName) const; - -private: - explicit CardOverrideSettings(const QString &settingPath, QObject *parent = nullptr); - CardOverrideSettings(const CardOverrideSettings & /*other*/); -}; - -#endif // COCKATRICE_CARD_OVERRIDE_SETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/debug_settings.cpp b/libcockatrice_settings/libcockatrice/settings/debug_settings.cpp deleted file mode 100644 index 5bf6eca30..000000000 --- a/libcockatrice_settings/libcockatrice/settings/debug_settings.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "debug_settings.h" - -#include - -DebugSettings::DebugSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "debug.ini", "debug", QString(), parent) -{ - // Create the default debug.ini if it doesn't exist yet - if (!QFile(settingPath + "debug.ini").exists()) { - QFile::copy(":/resources/config/debug.ini", settingPath + "debug.ini"); - } -} - -bool DebugSettings::getShowCardId() const -{ - return getValue("showCardId").toBool(); -} - -bool DebugSettings::getLocalGameOnStartup() const -{ - return getValue("onStartup", "localgame").toBool(); -} - -int DebugSettings::getLocalGamePlayerCount() const -{ - return getValue("playerCount", "localgame").toInt(); -} - -QString DebugSettings::getDeckPathForPlayer(const QString &playerName) const -{ - return getValue(playerName, "localgame", "deck").toString(); -} \ No newline at end of file diff --git a/libcockatrice_settings/libcockatrice/settings/debug_settings.h b/libcockatrice_settings/libcockatrice/settings/debug_settings.h deleted file mode 100644 index 30cdd5fa5..000000000 --- a/libcockatrice_settings/libcockatrice/settings/debug_settings.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file debug_settings.h - * @ingroup CoreSettings - * @brief TODO: Document this. - */ - -#ifndef DEBUG_SETTINGS_H -#define DEBUG_SETTINGS_H -#include "settings_manager.h" - -class DebugSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - - explicit DebugSettings(const QString &settingPath, QObject *parent = nullptr); - DebugSettings(const DebugSettings & /*other*/); - -public: - bool getShowCardId() const; - - bool getLocalGameOnStartup() const; - int getLocalGamePlayerCount() const; - - QString getDeckPathForPlayer(const QString &playerName) const; -}; - -#endif // DEBUG_SETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/download_settings.cpp b/libcockatrice_settings/libcockatrice/settings/download_settings.cpp deleted file mode 100644 index 66525a598..000000000 --- a/libcockatrice_settings/libcockatrice/settings/download_settings.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "download_settings.h" - -#include "settings_manager.h" - -const QStringList DownloadSettings::DEFAULT_DOWNLOAD_URLS = { - "https://api.scryfall.com/cards/!set:uuid!?format=image&face=!prop:side!", - "https://api.scryfall.com/cards/multiverse/!set:muid!?format=image", - "https://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=!set:muid!&type=card", - "https://gatherer.wizards.com/Handlers/Image.ashx?name=!name!&type=card"}; - -DownloadSettings::DownloadSettings(const QString &settingPath, QObject *parent = nullptr) - : SettingsManager(settingPath + "downloads.ini", "downloads", QString(), parent) -{ -} - -void DownloadSettings::setDownloadUrls(const QStringList &downloadURLs) -{ - setValue(QVariant::fromValue(downloadURLs), "urls"); -} - -QStringList DownloadSettings::getAllURLs() const -{ - return getValue("urls").toStringList(); -} - -void DownloadSettings::resetToDefaultURLs() -{ - setValue(QVariant::fromValue(DEFAULT_DOWNLOAD_URLS), "urls"); -} diff --git a/libcockatrice_settings/libcockatrice/settings/download_settings.h b/libcockatrice_settings/libcockatrice/settings/download_settings.h deleted file mode 100644 index b7442301e..000000000 --- a/libcockatrice_settings/libcockatrice/settings/download_settings.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file download_settings.h - * @ingroup NetworkSettings - * @brief TODO: Document this. - */ - -#ifndef COCKATRICE_DOWNLOADSETTINGS_H -#define COCKATRICE_DOWNLOADSETTINGS_H - -#include "settings_manager.h" - -class DownloadSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - - static const QStringList DEFAULT_DOWNLOAD_URLS; - -public: - explicit DownloadSettings(const QString &, QObject *); - - QStringList getAllURLs() const; - void setDownloadUrls(const QStringList &downloadURLs); - void resetToDefaultURLs(); -}; - -#endif // COCKATRICE_DOWNLOADSETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/game_filters_settings.cpp b/libcockatrice_settings/libcockatrice/settings/game_filters_settings.cpp deleted file mode 100644 index 4f5bf52ee..000000000 --- a/libcockatrice_settings/libcockatrice/settings/game_filters_settings.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "game_filters_settings.h" - -#include -#include - -GameFiltersSettings::GameFiltersSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "gamefilters.ini", "filter_games", QString(), parent) -{ -} - -/** - * The game type might contain special characters, so to use it in - * QSettings we just hash it. - */ -static QString hashGameType(const QString &gameType) -{ - return QCryptographicHash::hash(gameType.toUtf8(), QCryptographicHash::Md5).toHex(); -} - -void GameFiltersSettings::setHideBuddiesOnlyGames(bool hide) -{ - setValue(hide, "hide_buddies_only_games"); -} - -bool GameFiltersSettings::isHideBuddiesOnlyGames() const -{ - QVariant previous = getValue("hide_buddies_only_games"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setHideFullGames(bool hide) -{ - setValue(hide, "hide_full_games"); -} - -bool GameFiltersSettings::isHideFullGames() const -{ - QVariant previous = getValue("hide_full_games"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setHideGamesThatStarted(bool hide) -{ - setValue(hide, "hide_games_that_started"); -} - -bool GameFiltersSettings::isHideGamesThatStarted() const -{ - QVariant previous = getValue("hide_games_that_started"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setHidePasswordProtectedGames(bool hide) -{ - setValue(hide, "hide_password_protected_games"); -} - -bool GameFiltersSettings::isHidePasswordProtectedGames() const -{ - QVariant previous = getValue("hide_password_protected_games"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setHideIgnoredUserGames(bool hide) -{ - setValue(hide, "hide_ignored_user_games"); -} - -bool GameFiltersSettings::isHideIgnoredUserGames() const -{ - QVariant previous = getValue("hide_ignored_user_games"); - return previous == QVariant() ? true : previous.toBool(); -} - -void GameFiltersSettings::setHideNotBuddyCreatedGames(bool hide) -{ - setValue(hide, "hide_not_buddy_created_games"); -} - -bool GameFiltersSettings::isHideNotBuddyCreatedGames() const -{ - QVariant previous = getValue("hide_not_buddy_created_games"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setHideOpenDecklistGames(bool hide) -{ - setValue(hide, "hide_open_decklist_games"); -} - -bool GameFiltersSettings::isHideOpenDecklistGames() const -{ - QVariant previous = getValue("hide_open_decklist_games"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setGameNameFilter(QString gameName) -{ - setValue(gameName, "game_name_filter"); -} - -QString GameFiltersSettings::getGameNameFilter() const -{ - return getValue("game_name_filter").toString(); -} - -void GameFiltersSettings::setCreatorNameFilters(QStringList creatorName) -{ - setValue(creatorName, "creator_name_filter"); -} - -QStringList GameFiltersSettings::getCreatorNameFilters() const -{ - return getValue("creator_name_filter").toStringList(); -} - -void GameFiltersSettings::setMinPlayers(int min) -{ - setValue(min, "min_players"); -} - -int GameFiltersSettings::getMinPlayers() const -{ - QVariant previous = getValue("min_players"); - return previous == QVariant() ? 1 : previous.toInt(); -} - -void GameFiltersSettings::setMaxPlayers(int max) -{ - setValue(max, "max_players"); -} - -int GameFiltersSettings::getMaxPlayers() const -{ - QVariant previous = getValue("max_players"); - return previous == QVariant() ? 99 : previous.toInt(); -} - -void GameFiltersSettings::setMaxGameAge(const QTime &maxGameAge) -{ - setValue(maxGameAge, "max_game_age_time"); -} - -QTime GameFiltersSettings::getMaxGameAge() const -{ - QVariant previous = getValue("max_game_age_time"); - return previous.toTime(); -} - -void GameFiltersSettings::setGameTypeEnabled(QString gametype, bool enabled) -{ - setValue(enabled, "game_type/" + hashGameType(gametype)); -} - -void GameFiltersSettings::setGameHashedTypeEnabled(QString gametypeHASHED, bool enabled) -{ - setValue(enabled, gametypeHASHED); -} - -bool GameFiltersSettings::isGameTypeEnabled(QString gametype) const -{ - QVariant previous = getValue("game_type/" + hashGameType(gametype)); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setShowOnlyIfSpectatorsCanWatch(bool show) -{ - setValue(show, "show_only_if_spectators_can_watch"); -} - -bool GameFiltersSettings::isShowOnlyIfSpectatorsCanWatch() const -{ - QVariant previous = getValue("show_only_if_spectators_can_watch"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setShowSpectatorPasswordProtected(bool show) -{ - setValue(show, "show_spectator_password_protected"); -} - -bool GameFiltersSettings::isShowSpectatorPasswordProtected() const -{ - QVariant previous = getValue("show_spectator_password_protected"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setShowOnlyIfSpectatorsCanChat(bool show) -{ - setValue(show, "show_only_if_spectators_can_chat"); -} - -bool GameFiltersSettings::isShowOnlyIfSpectatorsCanChat() const -{ - QVariant previous = getValue("show_only_if_spectators_can_chat"); - return previous == QVariant() ? false : previous.toBool(); -} - -void GameFiltersSettings::setShowOnlyIfSpectatorsCanSeeHands(bool show) -{ - setValue(show, "show_only_if_spectators_can_see_hands"); -} - -bool GameFiltersSettings::isShowOnlyIfSpectatorsCanSeeHands() const -{ - QVariant previous = getValue("show_only_if_spectators_can_see_hands"); - return previous == QVariant() ? false : previous.toBool(); -} \ No newline at end of file diff --git a/libcockatrice_settings/libcockatrice/settings/game_filters_settings.h b/libcockatrice_settings/libcockatrice/settings/game_filters_settings.h deleted file mode 100644 index c0e60551a..000000000 --- a/libcockatrice_settings/libcockatrice/settings/game_filters_settings.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @file game_filters_settings.h - * @ingroup Lobby - * @ingroup GameSettings - * @brief TODO: Document this. - */ - -#ifndef GAMEFILTERSSETTINGS_H -#define GAMEFILTERSSETTINGS_H - -#include "settings_manager.h" - -class GameFiltersSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - -public: - bool isHideBuddiesOnlyGames() const; - bool isHideFullGames() const; - bool isHideGamesThatStarted() const; - bool isHidePasswordProtectedGames() const; - bool isHideIgnoredUserGames() const; - bool isHideNotBuddyCreatedGames() const; - bool isHideOpenDecklistGames() const; - QString getGameNameFilter() const; - QStringList getCreatorNameFilters() const; - int getMinPlayers() const; - int getMaxPlayers() const; - QTime getMaxGameAge() const; - bool isGameTypeEnabled(QString gametype) const; - bool isShowOnlyIfSpectatorsCanWatch() const; - bool isShowSpectatorPasswordProtected() const; - bool isShowOnlyIfSpectatorsCanChat() const; - bool isShowOnlyIfSpectatorsCanSeeHands() const; - - void setHideBuddiesOnlyGames(bool hide); - void setHideIgnoredUserGames(bool hide); - void setHideOpenDecklistGames(bool hide); - void setHideFullGames(bool hide); - void setHideGamesThatStarted(bool hide); - void setHidePasswordProtectedGames(bool hide); - void setHideNotBuddyCreatedGames(bool hide); - void setGameNameFilter(QString gameName); - void setCreatorNameFilters(QStringList creatorName); - void setMinPlayers(int min); - void setMaxPlayers(int max); - void setMaxGameAge(const QTime &maxGameAge); - void setGameTypeEnabled(QString gametype, bool enabled); - void setGameHashedTypeEnabled(QString gametypeHASHED, bool enabled); - void setShowOnlyIfSpectatorsCanWatch(bool show); - void setShowSpectatorPasswordProtected(bool show); - void setShowOnlyIfSpectatorsCanChat(bool show); - void setShowOnlyIfSpectatorsCanSeeHands(bool show); - -private: - explicit GameFiltersSettings(const QString &settingPath, QObject *parent = nullptr); - GameFiltersSettings(const GameFiltersSettings & /*other*/); -}; - -#endif // GAMEFILTERSSETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/layouts_settings.cpp b/libcockatrice_settings/libcockatrice/settings/layouts_settings.cpp deleted file mode 100644 index e914dc2d8..000000000 --- a/libcockatrice_settings/libcockatrice/settings/layouts_settings.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "layouts_settings.h" - -const static QString STATE_PROP = "state"; -const static QString GEOMETRY_PROP = "geometry"; -const static QString SIZE_PROP = "widgetSize"; - -const static QString GROUP_MAIN_WINDOW = "mainWindow"; -const static QString GROUP_DECK_EDITOR = "deckEditor"; -const static QString GROUP_VISUAL_DECK_EDITOR = "visualDeckEditor"; -const static QString GROUP_DECK_EDITOR_DB = "deckEditorDb"; -const static QString GROUP_SETS_DIALOG = "setsDialog"; -const static QString GROUP_TOKEN_DIALOG = "tokenDialog"; -const static QString GROUP_GAME_PLAY_AREA = "gamePlayArea"; -const static QString GROUP_REPLAY_PLAY_AREA = "replayPlayArea"; - -LayoutsSettings::LayoutsSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "layouts.ini", QString(), QString(), parent) -{ -} - -void LayoutsSettings::setMainWindowGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_MAIN_WINDOW); -} - -QByteArray LayoutsSettings::getMainWindowGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_MAIN_WINDOW).toByteArray(); -} - -QByteArray LayoutsSettings::getDeckEditorLayoutState() const -{ - return getValue(STATE_PROP, GROUP_DECK_EDITOR).toByteArray(); -} - -void LayoutsSettings::setDeckEditorLayoutState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_DECK_EDITOR); -} - -QByteArray LayoutsSettings::getDeckEditorGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_DECK_EDITOR).toByteArray(); -} - -void LayoutsSettings::setDeckEditorGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_DECK_EDITOR); -} - -QByteArray LayoutsSettings::getVisualDeckEditorLayoutState() const -{ - return getValue(STATE_PROP, GROUP_VISUAL_DECK_EDITOR).toByteArray(); -} - -void LayoutsSettings::setVisualDeckEditorLayoutState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_VISUAL_DECK_EDITOR); -} - -QByteArray LayoutsSettings::getVisualDeckEditorGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_VISUAL_DECK_EDITOR).toByteArray(); -} - -void LayoutsSettings::setVisualDeckEditorGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_VISUAL_DECK_EDITOR); -} - -QByteArray LayoutsSettings::getDeckEditorDbHeaderState() const -{ - return getValue(STATE_PROP, GROUP_DECK_EDITOR_DB, "header").toByteArray(); -} - -void LayoutsSettings::setDeckEditorDbHeaderState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_DECK_EDITOR_DB, "header"); -} - -QByteArray LayoutsSettings::getSetsDialogHeaderState() const -{ - return getValue(STATE_PROP, GROUP_SETS_DIALOG, "header").toByteArray(); -} - -void LayoutsSettings::setSetsDialogHeaderState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_SETS_DIALOG, "header"); -} - -void LayoutsSettings::setSetsDialogGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_SETS_DIALOG); -} - -QByteArray LayoutsSettings::getSetsDialogGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_SETS_DIALOG).toByteArray(); -} - -void LayoutsSettings::setTokenDialogGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_TOKEN_DIALOG); -} - -QByteArray LayoutsSettings::getTokenDialogGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_TOKEN_DIALOG).toByteArray(); -} - -void LayoutsSettings::setGamePlayAreaGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_GAME_PLAY_AREA); -} - -void LayoutsSettings::setGamePlayAreaState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_GAME_PLAY_AREA); -} - -QByteArray LayoutsSettings::getGamePlayAreaLayoutState() const -{ - return getValue(STATE_PROP, GROUP_GAME_PLAY_AREA).toByteArray(); -} - -QByteArray LayoutsSettings::getGamePlayAreaGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_GAME_PLAY_AREA).toByteArray(); -} - -void LayoutsSettings::setReplayPlayAreaGeometry(const QByteArray &value) -{ - setValue(value, GEOMETRY_PROP, GROUP_REPLAY_PLAY_AREA); -} - -void LayoutsSettings::setReplayPlayAreaState(const QByteArray &value) -{ - setValue(value, STATE_PROP, GROUP_REPLAY_PLAY_AREA); -} - -QByteArray LayoutsSettings::getReplayPlayAreaLayoutState() const -{ - return getValue(STATE_PROP, GROUP_REPLAY_PLAY_AREA).toByteArray(); -} - -QByteArray LayoutsSettings::getReplayPlayAreaGeometry() const -{ - return getValue(GEOMETRY_PROP, GROUP_REPLAY_PLAY_AREA).toByteArray(); -} diff --git a/libcockatrice_settings/libcockatrice/settings/layouts_settings.h b/libcockatrice_settings/libcockatrice/settings/layouts_settings.h deleted file mode 100644 index 5353ce15a..000000000 --- a/libcockatrice_settings/libcockatrice/settings/layouts_settings.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @file layouts_settings.h - * @ingroup CoreSettings - * @brief TODO: Document this. - */ - -#ifndef LAYOUTSSETTINGS_H -#define LAYOUTSSETTINGS_H - -#include "settings_manager.h" - -#include - -class LayoutsSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - -public: - void setMainWindowGeometry(const QByteArray &value); - - void setDeckEditorLayoutState(const QByteArray &value); - void setDeckEditorGeometry(const QByteArray &value); - - void setVisualDeckEditorLayoutState(const QByteArray &value); - void setVisualDeckEditorGeometry(const QByteArray &value); - - void setDeckEditorDbHeaderState(const QByteArray &value); - void setSetsDialogHeaderState(const QByteArray &value); - void setSetsDialogGeometry(const QByteArray &value); - void setTokenDialogGeometry(const QByteArray &value); - - void setGamePlayAreaGeometry(const QByteArray &value); - void setGamePlayAreaState(const QByteArray &value); - - void setReplayPlayAreaGeometry(const QByteArray &value); - void setReplayPlayAreaState(const QByteArray &value); - - QByteArray getMainWindowGeometry() const; - - QByteArray getDeckEditorLayoutState() const; - QByteArray getDeckEditorGeometry() const; - - QByteArray getVisualDeckEditorLayoutState() const; - QByteArray getVisualDeckEditorGeometry() const; - - QByteArray getDeckEditorDbHeaderState() const; - QByteArray getSetsDialogHeaderState() const; - QByteArray getSetsDialogGeometry() const; - QByteArray getTokenDialogGeometry() const; - - QByteArray getGamePlayAreaLayoutState() const; - QByteArray getGamePlayAreaGeometry() const; - - QByteArray getReplayPlayAreaLayoutState() const; - QByteArray getReplayPlayAreaGeometry() const; -signals: - -public slots: - -private: - explicit LayoutsSettings(const QString &settingPath, QObject *parent = nullptr); - LayoutsSettings(const LayoutsSettings & /*other*/); -}; - -#endif // LAYOUTSSETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/message_settings.cpp b/libcockatrice_settings/libcockatrice/settings/message_settings.cpp deleted file mode 100644 index 50da39df6..000000000 --- a/libcockatrice_settings/libcockatrice/settings/message_settings.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "message_settings.h" - -MessageSettings::MessageSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "messages.ini", "messages", QString(), parent) -{ -} - -QString MessageSettings::getMessageAt(int index) const -{ - return getValue(QString("msg%1").arg(index)).toString(); -} - -int MessageSettings::getCount() const -{ - return getValue("count").toInt(); -} - -void MessageSettings::setCount(int count) -{ - setValue(count, "count"); -} - -void MessageSettings::setMessageAt(int index, QString message) -{ - setValue(message, QString("msg%1").arg(index)); -} diff --git a/libcockatrice_settings/libcockatrice/settings/message_settings.h b/libcockatrice_settings/libcockatrice/settings/message_settings.h deleted file mode 100644 index ec70027af..000000000 --- a/libcockatrice_settings/libcockatrice/settings/message_settings.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @file message_settings.h - * @ingroup NetworkSettings - * @brief TODO: Document this. - */ - -#ifndef MESSAGESETTINGS_H -#define MESSAGESETTINGS_H - -#include "settings_manager.h" - -class MessageSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - -public: - int getCount() const; - QString getMessageAt(int index) const; - - void setCount(int count); - void setMessageAt(int index, QString message); -signals: - void messageMacrosChanged(); - -public slots: - -private: - explicit MessageSettings(const QString &settingPath, QObject *parent = nullptr); - MessageSettings(const MessageSettings & /*other*/); -}; - -#endif // MESSAGESETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/recents_settings.cpp b/libcockatrice_settings/libcockatrice/settings/recents_settings.cpp deleted file mode 100644 index 76bc4069e..000000000 --- a/libcockatrice_settings/libcockatrice/settings/recents_settings.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "recents_settings.h" - -#define MAX_RECENT_DECK_COUNT 10 - -RecentsSettings::RecentsSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "recents.ini", "deckbuilder", QString(), parent) -{ -} - -QStringList RecentsSettings::getRecentlyOpenedDeckPaths() const -{ - return getValue("deckpaths").toStringList(); -} -void RecentsSettings::clearRecentlyOpenedDeckPaths() -{ - deleteValue("deckpaths"); - emit recentlyOpenedDeckPathsChanged(); -} -void RecentsSettings::updateRecentlyOpenedDeckPaths(const QString &deckPath) -{ - auto deckPaths = getValue("deckpaths").toStringList(); - deckPaths.removeAll(deckPath); - - deckPaths.prepend(deckPath); - - while (deckPaths.size() > MAX_RECENT_DECK_COUNT) { - deckPaths.removeLast(); - } - - setValue(deckPaths, "deckpaths"); - emit recentlyOpenedDeckPathsChanged(); -} - -QString RecentsSettings::getLatestDeckDirPath() const -{ - return getValue("latestDeckDir", "dirs").toString(); -} - -void RecentsSettings::setLatestDeckDirPath(const QString &dirPath) -{ - setValue(dirPath, "latestDeckDir", "dirs"); -} \ No newline at end of file diff --git a/libcockatrice_settings/libcockatrice/settings/recents_settings.h b/libcockatrice_settings/libcockatrice/settings/recents_settings.h deleted file mode 100644 index 3aebff334..000000000 --- a/libcockatrice_settings/libcockatrice/settings/recents_settings.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file recents_settings.h - * @ingroup DeckSettings - * @brief TODO: Document this. - */ - -#ifndef RECENTS_SETTINGS_H -#define RECENTS_SETTINGS_H - -#include "settings_manager.h" - -class RecentsSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - - explicit RecentsSettings(const QString &settingPath, QObject *parent = nullptr); - RecentsSettings(const RecentsSettings & /*other*/); - -public: - QStringList getRecentlyOpenedDeckPaths() const; - void clearRecentlyOpenedDeckPaths(); - void updateRecentlyOpenedDeckPaths(const QString &deckPath); - - QString getLatestDeckDirPath() const; - void setLatestDeckDirPath(const QString &dirPath); - -signals: - void recentlyOpenedDeckPathsChanged(); -}; - -#endif // RECENTS_SETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/servers_settings.cpp b/libcockatrice_settings/libcockatrice/settings/servers_settings.cpp deleted file mode 100644 index 0140182be..000000000 --- a/libcockatrice_settings/libcockatrice/settings/servers_settings.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "servers_settings.h" - -#include -#include - -ServersSettings::ServersSettings(const QString &settingPath, QObject *parent) - : SettingsManager(settingPath + "servers.ini", "server", QString(), parent) -{ -} - -void ServersSettings::setPreviousHostLogin(int previous) -{ - setValue(previous, "previoushostlogin"); -} - -int ServersSettings::getPreviousHostLogin() const -{ - QVariant previous = getValue("previoushostlogin"); - return previous == QVariant() ? 1 : previous.toInt(); -} - -void ServersSettings::setPreviousHostList(QStringList list) -{ - setValue(list, "previoushosts"); -} - -QStringList ServersSettings::getPreviousHostList() const -{ - return getValue("previoushosts").toStringList(); -} - -void ServersSettings::setPrevioushostName(const QString &name) -{ - setValue(name, "previoushostName"); -} - -QString ServersSettings::getSaveName(QString defaultname) -{ - int index = getPrevioushostindex(getPrevioushostName()); - QVariant saveName = getValue(QString("saveName%1").arg(index), "server", "server_details"); - return saveName == QVariant() ? std::move(defaultname) : saveName.toString(); -} - -QString ServersSettings::getSite(QString defaultSite) -{ - int index = getPrevioushostindex(getPrevioushostName()); - QVariant site = getValue(QString("site%1").arg(index), "server", "server_details"); - return site == QVariant() ? std::move(defaultSite) : site.toString(); -} - -QString ServersSettings::getPrevioushostName() const -{ - QVariant value = getValue("previoushostName"); - return value == QVariant() ? "Rooster Ranges" : value.toString(); -} - -int ServersSettings::getPrevioushostindex(const QString &saveName) const -{ - int size = getValue("totalServers", "server", "server_details").toInt(); - - for (int i = 0; i <= size; ++i) - if (saveName == getValue(QString("saveName%1").arg(i), "server", "server_details").toString()) - return i; - - return -1; -} - -QString ServersSettings::getHostname(QString defaultHost) const -{ - int index = getPrevioushostindex(getPrevioushostName()); - QVariant hostname = getValue(QString("server%1").arg(index), "server", "server_details"); - return hostname == QVariant() ? std::move(defaultHost) : hostname.toString(); -} - -QString ServersSettings::getPort(QString defaultPort) const -{ - int index = getPrevioushostindex(getPrevioushostName()); - QVariant port = getValue(QString("port%1").arg(index), "server", "server_details"); - qCDebug(ServersSettingsLog) << "getPort() index = " << index << " port.val = " << port.toString(); - return port == QVariant() ? std::move(defaultPort) : port.toString(); -} - -QString ServersSettings::getPlayerName(QString defaultName) const -{ - int index = getPrevioushostindex(getPrevioushostName()); - QVariant name = getValue(QString("username%1").arg(index), "server", "server_details"); - qCDebug(ServersSettingsLog) << "getPlayerName() index = " << index << " name.val = " << name.toString(); - return name == QVariant() ? std::move(defaultName) : name.toString(); -} - -QString ServersSettings::getPassword() -{ - int index = getPrevioushostindex(getPrevioushostName()); - - if (getSavePassword()) - return getValue(QString("password%1").arg(index), "server", "server_details").toString(); - - return QString(); -} - -bool ServersSettings::getSavePassword() const -{ - int index = getPrevioushostindex(getPrevioushostName()); - bool save = getValue(QString("savePassword%1").arg(index), "server", "server_details").toBool(); - return save; -} - -void ServersSettings::setAutoConnect(int autoconnect) -{ - setValue(autoconnect, "auto_connect"); -} - -int ServersSettings::getAutoConnect() const -{ - QVariant autoconnect = getValue("auto_connect"); - return autoconnect == QVariant() ? 0 : autoconnect.toInt(); -} - -void ServersSettings::setFPHostName(QString hostname) -{ - setValue(hostname, "fphostname"); -} - -QString ServersSettings::getFPHostname(QString defaultHost) const -{ - QVariant hostname = getValue("fphostname"); - return hostname == QVariant() ? std::move(defaultHost) : hostname.toString(); -} - -void ServersSettings::setFPPort(QString port) -{ - setValue(port, "fpport"); -} - -QString ServersSettings::getFPPort(QString defaultPort) const -{ - QVariant port = getValue("fpport"); - return port == QVariant() ? std::move(defaultPort) : port.toString(); -} - -void ServersSettings::setFPPlayerName(QString playerName) -{ - setValue(playerName, "fpplayername"); -} - -QString ServersSettings::getFPPlayerName(QString defaultName) const -{ - QVariant name = getValue("fpplayername"); - return name == QVariant() ? std::move(defaultName) : name.toString(); -} - -void ServersSettings::setClearDebugLogStatus(bool abIsChecked) -{ - setValue(abIsChecked, "save_debug_log"); -} - -bool ServersSettings::getClearDebugLogStatus(bool abDefaultValue) const -{ - QVariant cbFlushLog = getValue("save_debug_log"); - return cbFlushLog == QVariant() ? abDefaultValue : cbFlushLog.toBool(); -} - -void ServersSettings::addNewServer(const QString &saveName, - const QString &serv, - const QString &port, - const QString &username, - const QString &password, - bool savePassword, - const QString &site) -{ - if (updateExistingServer(saveName, serv, port, username, password, savePassword, site)) - return; - - int index = getValue("totalServers", "server", "server_details").toInt() + 1; - - setValue(saveName, QString("saveName%1").arg(index), "server", "server_details"); - setValue(serv, QString("server%1").arg(index), "server", "server_details"); - setValue(port, QString("port%1").arg(index), "server", "server_details"); - setValue(username, QString("username%1").arg(index), "server", "server_details"); - setValue(savePassword, QString("savePassword%1").arg(index), "server", "server_details"); - setValue(index, "totalServers", "server", "server_details"); - setValue(password, QString("password%1").arg(index), "server", "server_details"); - setValue(site, QString("site%1").arg(index), "server", "server_details"); -} - -void ServersSettings::removeServer(QString servAddr) -{ - int size = getValue("totalServers", "server", "server_details").toInt(); - - bool found = false; - for (int i = 0; i <= size; ++i) { - if (!found) { - // find entry and overwrite it - if (servAddr == getValue(QString("server%1").arg(i), "server", "server_details").toString()) { - found = true; - } - } else { - // move all other entries after it one back, overwriting the previous one - int previous = i - 1; // we delete only one entry - setValue(getValue(QString("server%1").arg(i), "server", "server_details"), - QString("server%1").arg(previous), "server", "server_details"); - setValue(getValue(QString("port%1").arg(i), "server", "server_details"), QString("port%1").arg(previous), - "server", "server_details"); - setValue(getValue(QString("username%1").arg(i), "server", "server_details"), - QString("username%1").arg(previous), "server", "server_details"); - setValue(getValue(QString("savePassword%1").arg(i), "server", "server_details"), - QString("savePassword%1").arg(previous), "server", "server_details"); - setValue(getValue(QString("password%1").arg(i), "server", "server_details"), - QString("password%1").arg(previous), "server", "server_details"); - setValue(getValue(QString("saveName%1").arg(i), "server", "server_details"), - QString("saveName%1").arg(previous), "server", "server_details"); - setValue(getValue(QString("site%1").arg(i), "server", "server_details"), QString("site%1").arg(previous), - "server", "server_details"); - } - } - - // if we have deleted an entry, adjust the total - if (found) { - setValue(size - 1, "totalServers", "server", "server_details"); - - // delete last value - deleteValue(QString("server%1").arg(size), "server", "server_details"); - deleteValue(QString("port%1").arg(size), "server", "server_details"); - deleteValue(QString("username%1").arg(size), "server", "server_details"); - deleteValue(QString("savePassword%1").arg(size), "server", "server_details"); - deleteValue(QString("password%1").arg(size), "server", "server_details"); - deleteValue(QString("saveName%1").arg(size), "server", "server_details"); - deleteValue(QString("site%1").arg(size), "server", "server_details"); - } -} - -/** - * Will only update fields with new values, ignores empty values - */ -bool ServersSettings::updateExistingServerWithoutLoss(QString saveName, QString serv, QString port, QString site) -{ - int size = getValue("totalServers", "server", "server_details").toInt(); - - for (int i = 0; i <= size; ++i) { - if (serv == getValue(QString("server%1").arg(i), "server", "server_details").toString()) { - if (!port.isEmpty()) { - setValue(port, QString("port%1").arg(i), "server", "server_details"); - } - - if (!site.isEmpty()) { - setValue(site, QString("site%1").arg(i), "server", "server_details"); - } - - setValue(saveName, QString("saveName%1").arg(i), "server", "server_details"); - - return true; - } - } - return false; -} - -bool ServersSettings::updateExistingServer(QString saveName, - QString serv, - QString port, - QString username, - QString password, - bool savePassword, - QString site) -{ - int size = getValue("totalServers", "server", "server_details").toInt(); - - for (int i = 0; i <= size; ++i) { - if (serv == getValue(QString("server%1").arg(i), "server", "server_details").toString()) { - setValue(port, QString("port%1").arg(i), "server", "server_details"); - if (!username.isEmpty()) { - setValue(username, QString("username%1").arg(i), "server", "server_details"); - } - - if (savePassword && !password.isEmpty()) { - setValue(password, QString("password%1").arg(i), "server", "server_details"); - } else { - setValue(QString(), QString("password%1").arg(i), "server", "server_details"); - } - - if (!site.isEmpty()) { - setValue(site, QString("site%1").arg(i), "server", "server_details"); - } - - setValue(savePassword, QString("savePassword%1").arg(i), "server", "server_details"); - setValue(saveName, QString("saveName%1").arg(i), "server", "server_details"); - - return true; - } - } - return false; -} diff --git a/libcockatrice_settings/libcockatrice/settings/servers_settings.h b/libcockatrice_settings/libcockatrice/settings/servers_settings.h deleted file mode 100644 index 22603a356..000000000 --- a/libcockatrice_settings/libcockatrice/settings/servers_settings.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @file servers_settings.h - * @ingroup NetworkSettings - * @brief TODO: Document this. - */ - -#ifndef SERVERSSETTINGS_H -#define SERVERSSETTINGS_H - -#include "settings_manager.h" - -#include -#include -#define SERVERSETTINGS_DEFAULT_HOST "server.cockatrice.us" -#define SERVERSETTINGS_DEFAULT_PORT "4748" - -inline Q_LOGGING_CATEGORY(ServersSettingsLog, "servers_settings"); - -class ServersSettings : public SettingsManager -{ - Q_OBJECT - friend class SettingsCache; - -public: - int getPreviousHostLogin() const; - int getPrevioushostindex(const QString &) const; - QStringList getPreviousHostList() const; - QString getPrevioushostName() const; - QString getHostname(QString defaultHost = SERVERSETTINGS_DEFAULT_HOST) const; - QString getPort(QString defaultPort = SERVERSETTINGS_DEFAULT_PORT) const; - QString getPlayerName(QString defaultName = "") const; - QString getFPHostname(QString defaultHost = SERVERSETTINGS_DEFAULT_HOST) const; - QString getFPPort(QString defaultPort = SERVERSETTINGS_DEFAULT_PORT) const; - QString getFPPlayerName(QString defaultName = "") const; - QString getPassword(); - QString getSaveName(QString defaultname = ""); - QString getSite(QString defaultName = ""); - bool getSavePassword() const; - int getAutoConnect() const; - - void setPreviousHostLogin(int previous); - void setPrevioushostName(const QString &); - void setPreviousHostList(QStringList list); - void setAutoConnect(int autoconnect); - void setSite(QString site); - void setFPHostName(QString hostname); - void setFPPort(QString port); - void setFPPlayerName(QString playerName); - void addNewServer(const QString &saveName, - const QString &serv, - const QString &port, - const QString &username, - const QString &password, - bool savePassword, - const QString &site = QString()); - void removeServer(QString servAddr); - bool updateExistingServer(QString saveName, - QString serv, - QString port, - QString username, - QString password, - bool savePassword, - QString site = QString()); - - bool updateExistingServerWithoutLoss(QString saveName, - QString serv = QString(), - QString port = QString(), - QString site = QString()); - void setClearDebugLogStatus(bool abIsChecked); - bool getClearDebugLogStatus(bool abDefaultValue) const; - -private: - explicit ServersSettings(const QString &settingPath, QObject *parent = nullptr); - ServersSettings(const ServersSettings & /*other*/); -}; - -#endif // SERVERSSETTINGS_H diff --git a/libcockatrice_settings/libcockatrice/settings/settings_manager.cpp b/libcockatrice_settings/libcockatrice/settings/settings_manager.cpp deleted file mode 100644 index 2d4f1c441..000000000 --- a/libcockatrice_settings/libcockatrice/settings/settings_manager.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include "settings_manager.h" - -SettingsManager::SettingsManager(const QString &_settingPath, - const QString &_defaultGroup, - const QString &_defaultSubGroup, - QObject *parent) - : QObject(parent), settingPath(_settingPath), defaultGroup(_defaultGroup), defaultSubGroup(_defaultSubGroup) -{ -} - -QSettings SettingsManager::getSettings() const -{ - return QSettings(settingPath, QSettings::IniFormat); -} - -void SettingsManager::setValue(const QVariant &value, const QString &name) -{ - auto settings = getSettings(); - - if (!defaultGroup.isEmpty()) { - settings.beginGroup(defaultGroup); - } - - if (!defaultSubGroup.isEmpty()) { - settings.beginGroup(defaultSubGroup); - } - - settings.setValue(name, value); - - if (!defaultSubGroup.isEmpty()) { - settings.endGroup(); - } - - if (!defaultGroup.isEmpty()) { - settings.endGroup(); - } -} - -void SettingsManager::setValue(const QVariant &value, - const QString &name, - const QString &group, - const QString &subGroup) -{ - auto settings = getSettings(); - - if (!group.isEmpty()) { - settings.beginGroup(group); - } - - if (!subGroup.isEmpty()) { - settings.beginGroup(subGroup); - } - - settings.setValue(name, value); - - if (!subGroup.isEmpty()) { - settings.endGroup(); - } - - if (!group.isEmpty()) { - settings.endGroup(); - } -} - -void SettingsManager::deleteValue(const QString &name) -{ - auto settings = getSettings(); - - if (!defaultGroup.isEmpty()) { - settings.beginGroup(defaultGroup); - } - - if (!defaultSubGroup.isEmpty()) { - settings.beginGroup(defaultSubGroup); - } - - settings.remove(name); - - if (!defaultSubGroup.isEmpty()) { - settings.endGroup(); - } - - if (!defaultGroup.isEmpty()) { - settings.endGroup(); - } -} - -void SettingsManager::deleteValue(const QString &name, const QString &group, const QString &subGroup) -{ - auto settings = getSettings(); - - if (!group.isEmpty()) { - settings.beginGroup(group); - } - - if (!subGroup.isEmpty()) { - settings.beginGroup(subGroup); - } - - settings.remove(name); - - if (!subGroup.isEmpty()) { - settings.endGroup(); - } - - if (!group.isEmpty()) { - settings.endGroup(); - } -} - -QVariant SettingsManager::getValue(const QString &name) const -{ - auto settings = getSettings(); - - if (!defaultGroup.isEmpty()) { - settings.beginGroup(defaultGroup); - } - - if (!defaultSubGroup.isEmpty()) { - settings.beginGroup(defaultSubGroup); - } - - QVariant value = settings.value(name); - - if (!defaultSubGroup.isEmpty()) { - settings.endGroup(); - } - - if (!defaultGroup.isEmpty()) { - settings.endGroup(); - } - - return value; -} - -QVariant SettingsManager::getValue(const QString &name, const QString &group, const QString &subGroup) const -{ - auto settings = getSettings(); - - if (!group.isEmpty()) { - settings.beginGroup(group); - } - - if (!subGroup.isEmpty()) { - settings.beginGroup(subGroup); - } - - QVariant value = settings.value(name); - - if (!subGroup.isEmpty()) { - settings.endGroup(); - } - - if (!group.isEmpty()) { - settings.endGroup(); - } - - return value; -} - -/** - * Calls sync on the underlying QSettings object - */ -void SettingsManager::sync() -{ - auto settings = getSettings(); - - settings.sync(); -} \ No newline at end of file diff --git a/libcockatrice_settings/libcockatrice/settings/settings_manager.h b/libcockatrice_settings/libcockatrice/settings/settings_manager.h deleted file mode 100644 index ad828f089..000000000 --- a/libcockatrice_settings/libcockatrice/settings/settings_manager.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file settings_manager.h - * @ingroup Settings - * @brief TODO: Document this. - */ - -#ifndef SETTINGSMANAGER_H -#define SETTINGSMANAGER_H - -#include -#include -#include - -class SettingsManager : public QObject -{ - Q_OBJECT -public: - explicit SettingsManager(const QString &settingPath, - const QString &defaultGroup = QString(), - const QString &defaultSubGroup = QString(), - QObject *parent = nullptr); - QVariant getValue(const QString &name) const; - QVariant getValue(const QString &name, const QString &group, const QString &subGroup = QString()) const; - void sync(); - -protected: - QString settingPath; - QString defaultGroup; - QString defaultSubGroup; - - QSettings getSettings() const; - - void setValue(const QVariant &value, const QString &name); - void - setValue(const QVariant &value, const QString &name, const QString &group, const QString &subGroup = QString()); - void deleteValue(const QString &name); - void deleteValue(const QString &name, const QString &group, const QString &subGroup = QString()); -}; - -#endif // SETTINGSMANAGER_H diff --git a/libcockatrice_utility/CMakeLists.txt b/libcockatrice_utility/CMakeLists.txt deleted file mode 100644 index c0c7d8cc9..000000000 --- a/libcockatrice_utility/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(Utility VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(UTILITY_SOURCES libcockatrice/utility/expression.cpp libcockatrice/utility/levenshtein.cpp - libcockatrice/utility/passwordhasher.cpp -) - -set(UTILITY_HEADERS - libcockatrice/utility/color.h - libcockatrice/utility/expression.h - libcockatrice/utility/levenshtein.h - libcockatrice/utility/macros.h - libcockatrice/utility/passwordhasher.h - libcockatrice/utility/trice_limits.h - libcockatrice/utility/zone_names.h -) - -add_library(libcockatrice_utility STATIC ${UTILITY_SOURCES} ${UTILITY_HEADERS}) - -target_include_directories(libcockatrice_utility PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_link_libraries(libcockatrice_utility PUBLIC libcockatrice_rng ${QT_CORE_MODULE}) - -set(ORACLE_LIBS) - -include_directories(${${COCKATRICE_QT_VERSION_NAME}Core_INCLUDE_DIRS}) diff --git a/libcockatrice_utility/libcockatrice/utility/card_ref.h b/libcockatrice_utility/libcockatrice/utility/card_ref.h deleted file mode 100644 index d89fe590b..000000000 --- a/libcockatrice_utility/libcockatrice/utility/card_ref.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CARD_REF_H -#define CARD_REF_H - -#include - -/** - * The information passed over the server that is required to identify the exact card to display. - * - * @param name The name of the card. Should not be empty, unless to indicate the lack of a card. - * @param providerId Determines which printing of the card to use. Can be empty, in which case Cockatrice should default - * to using the preferred set. - */ -struct CardRef -{ - QString name; - QString providerId = QString(); - - bool operator==(const CardRef &other) const - { - return name == other.name && providerId == other.providerId; - } - - bool isEmpty() const - { - return name.isEmpty() && providerId.isEmpty(); - } -}; - -#endif // CARD_REF_H diff --git a/libcockatrice_utility/libcockatrice/utility/color.h b/libcockatrice_utility/libcockatrice/utility/color.h deleted file mode 100644 index f02df3a0e..000000000 --- a/libcockatrice_utility/libcockatrice/utility/color.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef COLOR_H -#define COLOR_H - -#ifdef QT_GUI_LIB -#include -#endif - -#include - -#ifdef QT_GUI_LIB -inline QColor convertColorToQColor(const color &c) -{ - return QColor(c.r(), c.g(), c.b()); -} - -inline color convertQColorToColor(const QColor &c) -{ - color result; - result.set_r(c.red()); - result.set_g(c.green()); - result.set_b(c.blue()); - return result; -} - -#include - -namespace GameSpecificColors -{ -namespace MTG -{ -inline QColor colorHelper(const QString &name) -{ - static const QMap colorMap = { - {"W", QColor(245, 245, 220)}, - {"U", QColor(80, 140, 255)}, - {"B", QColor(60, 60, 60)}, - {"R", QColor(220, 60, 50)}, - {"G", QColor(70, 160, 70)}, - {"Creature", QColor(70, 130, 180)}, - {"Instant", QColor(138, 43, 226)}, - {"Sorcery", QColor(199, 21, 133)}, - {"Enchantment", QColor(218, 165, 32)}, - {"Artifact", QColor(169, 169, 169)}, - {"Planeswalker", QColor(210, 105, 30)}, - {"Land", QColor(110, 80, 50)}, - }; - - if (colorMap.contains(name)) - return colorMap[name]; - - if (name.length() == 1 && colorMap.contains(name.toUpper())) - return colorMap[name.toUpper()]; - - uint h = qHash(name); - int r = 100 + (h % 120); - int g = 100 + ((h >> 8) % 120); - int b = 100 + ((h >> 16) % 120); - - return QColor(r, g, b); -} - -inline QList> sortManaMapWUBRGCFirst(const QMap &input) -{ - static const QStringList priorityOrder = {"W", "U", "B", "R", "G", "C"}; - - QList> result; - QSet consumed; - - // 1. Add priority colors in fixed order - for (const QString &key : priorityOrder) { - auto it = input.find(key); - if (it != input.end()) { - result.append({it.key(), it.value()}); - consumed.insert(it.key()); - } - } - - // 2. Add remaining keys (QMap iteration is already sorted) - for (auto it = input.begin(); it != input.end(); ++it) { - if (!consumed.contains(it.key())) { - result.append({it.key(), it.value()}); - } - } - - return result; -} -} // namespace MTG -} // namespace GameSpecificColors -#endif - -inline color makeColor(int r, int g, int b) -{ - color result; - result.set_r(r); - result.set_g(g); - result.set_b(b); - return result; -} - -#endif diff --git a/libcockatrice_utility/libcockatrice/utility/expression.cpp b/libcockatrice_utility/libcockatrice/utility/expression.cpp deleted file mode 100644 index 42073670c..000000000 --- a/libcockatrice_utility/libcockatrice/utility/expression.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "expression.h" - -#include "peglib.h" - -#include -#include -#include -#include - -peg::parser math(R"( - EXPRESSION <- P0 - P0 <- P1 (P1_OPERATOR P1)* - P1 <- P2 (P2_OPERATOR P2)* - P2 <- P3 (P3_OPERATOR P3)* - P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')' - - P1_OPERATOR <- < [-+] > - P2_OPERATOR <- < [/*] > - P3_OPERATOR <- < '^' > - - NUMBER <- < '-'? [0-9]+ > - NAME <- < [a-z][a-z0-9]* > - VARIABLE <- < [xX] > - FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')' - - %whitespace <- [ \t\r]* - )"); - -QMap> *default_functions = nullptr; - -Expression::Expression(double initial) : value(initial) -{ - if (default_functions == nullptr) { - default_functions = new QMap>(); - default_functions->insert("abs", [](double a) { return qFabs(a); }); - default_functions->insert("ceil", [](double a) { return qCeil(a); }); - default_functions->insert("cos", [](double a) { return qCos(a); }); - default_functions->insert("floor", [](double a) { return qFloor(a); }); - default_functions->insert("log", [](double a) { return qLn(a); }); - default_functions->insert("log10", [](double a) { return qLn(a); }); - default_functions->insert("round", [](double a) { return qRound(a); }); - default_functions->insert("sin", [](double a) { return qSin(a); }); - default_functions->insert("sqrt", [](double a) { return qSqrt(a); }); - default_functions->insert("tan", [](double a) { return qTan(a); }); - default_functions->insert("trunc", [](double a) { return std::trunc(a); }); - } - fns = QMap>(*default_functions); -} - -double Expression::eval(const peg::Ast &ast) -{ - const auto &nodes = ast.nodes; - if (ast.name == "NUMBER") { - return stod(std::string(ast.token)); - } else if (ast.name == "FUNCTION") { - QString name = QString::fromStdString(std::string(nodes[0]->token)); - if (!fns.contains(name)) - return 0; - return fns[name](eval(*nodes[1])); - } else if (ast.name == "VARIABLE") { - return value; - } else if (ast.name[0] == 'P') { - double result = eval(*nodes[0]); - for (unsigned int i = 1; i < nodes.size(); i += 2) { - double arg = eval(*nodes[i + 1]); - char operation = nodes[i]->token[0]; - switch (operation) { - case '+': - result += arg; - break; - case '-': - result -= arg; - break; - case '*': - result *= arg; - break; - case '/': - result /= arg; - break; - case '^': - result = qPow(result, arg); - break; - default: - result = 0; - break; - } - } - return result; - } else { - return -1; - } -} - -double Expression::parse(const QString &expr) -{ - QByteArray ba = expr.toUtf8(); - - math.enable_ast(); - - std::shared_ptr ast; - if (math.parse(ba.data(), ast)) { - ast = peg::AstOptimizer(true).optimize(ast); - return eval(*ast); - } - - return 0; -} diff --git a/libcockatrice_utility/libcockatrice/utility/expression.h b/libcockatrice_utility/libcockatrice/utility/expression.h deleted file mode 100644 index 8c6a94932..000000000 --- a/libcockatrice_utility/libcockatrice/utility/expression.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef EXPRESSION_H -#define EXPRESSION_H - -#include -#include -#include - -namespace peg -{ -template struct AstBase; -struct EmptyType; -typedef AstBase Ast; -} // namespace peg - -class Expression -{ -public: - double value; - - explicit Expression(double initial = 0); - double parse(const QString &expr); - -private: - double eval(const peg::Ast &ast); - QMap> fns; -}; - -#endif diff --git a/libcockatrice_utility/libcockatrice/utility/levenshtein.cpp b/libcockatrice_utility/libcockatrice/utility/levenshtein.cpp deleted file mode 100644 index cfb972f91..000000000 --- a/libcockatrice_utility/libcockatrice/utility/levenshtein.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "levenshtein.h" - -#include -#include - -int levenshteinDistance(const QString &s1, const QString &s2) -{ - int len1 = s1.size(); - int len2 = s2.size(); - std::vector> dp(len1 + 1, std::vector(len2 + 1)); - - for (int i = 0; i <= len1; i++) - dp[i][0] = i; - for (int j = 0; j <= len2; j++) - dp[0][j] = j; - - for (int i = 1; i <= len1; i++) { - for (int j = 1; j <= len2; j++) { - int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1; - dp[i][j] = std::min({dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost}); - } - } - - return dp[len1][len2]; -} diff --git a/libcockatrice_utility/libcockatrice/utility/levenshtein.h b/libcockatrice_utility/libcockatrice/utility/levenshtein.h deleted file mode 100644 index e83235470..000000000 --- a/libcockatrice_utility/libcockatrice/utility/levenshtein.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @file levenshtein.h - * @ingroup Core - * @brief TODO: Document this. - */ - -#ifndef LEVENSHTEIN_H -#define LEVENSHTEIN_H - -#include - -int levenshteinDistance(const QString &s1, const QString &s2); - -#endif // LEVENSHTEIN_H diff --git a/libcockatrice_utility/libcockatrice/utility/macros.h b/libcockatrice_utility/libcockatrice/utility/macros.h deleted file mode 100644 index 1d7d6d632..000000000 --- a/libcockatrice_utility/libcockatrice/utility/macros.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef COCKATRICE_MACROS_H -#define COCKATRICE_MACROS_H - -#include - -// Qt6.7 changed how stateChanged functionality -// of QCheckBoxes work. -// See https://doc.qt.io/qt-6/qcheckbox.html#checkStateChanged -#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) -#define QT_STATE_CHANGED checkStateChanged -#define QT_STATE_CHANGED_T Qt::CheckState -#else -#define QT_STATE_CHANGED stateChanged -#define QT_STATE_CHANGED_T int -#endif - -#endif // COCKATRICE_MACROS_H diff --git a/libcockatrice_utility/libcockatrice/utility/passwordhasher.cpp b/libcockatrice_utility/libcockatrice/utility/passwordhasher.cpp deleted file mode 100644 index c40c5f94f..000000000 --- a/libcockatrice_utility/libcockatrice/utility/passwordhasher.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "passwordhasher.h" - -#include -#include - -QString PasswordHasher::computeHash(const QString &password, const QString &salt) -{ - QCryptographicHash::Algorithm algo = QCryptographicHash::Sha512; - const int rounds = 1000; - - QByteArray hash = (salt + password).toUtf8(); - for (int i = 0; i < rounds; ++i) { - hash = QCryptographicHash::hash(hash, algo); - } - QString hashedPass = salt + QString(hash.toBase64()); - return hashedPass; -} - -QString PasswordHasher::generateRandomSalt(const int len) -{ - static const char alphanum[] = "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; - - QString ret; - int size = sizeof(alphanum) - 1; - - for (int i = 0; i < len; ++i) { - ret.append(alphanum[rng->rand(0, size)]); - } - - return ret; -} - -QString PasswordHasher::generateActivationToken() -{ - return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16); -} diff --git a/libcockatrice_utility/libcockatrice/utility/passwordhasher.h b/libcockatrice_utility/libcockatrice/utility/passwordhasher.h deleted file mode 100644 index 811ecef15..000000000 --- a/libcockatrice_utility/libcockatrice/utility/passwordhasher.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PASSWORDHASHER_H -#define PASSWORDHASHER_H - -#include - -class PasswordHasher -{ -public: - static QString computeHash(const QString &password, const QString &salt); - static QString generateRandomSalt(const int len = 16); - static QString generateActivationToken(); -}; - -#endif diff --git a/libcockatrice_utility/libcockatrice/utility/peglib.h b/libcockatrice_utility/libcockatrice/utility/peglib.h deleted file mode 100644 index 3ae6040c4..000000000 --- a/libcockatrice_utility/libcockatrice/utility/peglib.h +++ /dev/null @@ -1,5758 +0,0 @@ -// -// peglib.h -// -// Copyright (c) 2022 Yuji Hirose. All rights reserved. -// MIT License -// - -#pragma once - -/* - * Configuration - */ - -#ifndef CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT -#define CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT 32 -#endif - -#include -#include -#include -#include -#include -#if __has_include() -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(__cplusplus) || __cplusplus < 201703L -#error "Requires complete C++17 support" -#endif - -namespace peg { - -/*----------------------------------------------------------------------------- - * scope_exit - *---------------------------------------------------------------------------*/ - -// This is based on -// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". - -template struct scope_exit { - explicit scope_exit(EF &&f) - : exit_function(std::move(f)), execute_on_destruction{true} {} - - scope_exit(scope_exit &&rhs) - : exit_function(std::move(rhs.exit_function)), - execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); - } - - ~scope_exit() { - if (execute_on_destruction) { this->exit_function(); } - } - - void release() { this->execute_on_destruction = false; } - -private: - scope_exit(const scope_exit &) = delete; - void operator=(const scope_exit &) = delete; - scope_exit &operator=(scope_exit &&) = delete; - - EF exit_function; - bool execute_on_destruction; -}; - -/*----------------------------------------------------------------------------- - * UTF8 functions - *---------------------------------------------------------------------------*/ - -inline size_t codepoint_length(const char *s8, size_t l) { - if (l) { - auto b = static_cast(s8[0]); - if ((b & 0x80) == 0) { - return 1; - } else if ((b & 0xE0) == 0xC0 && l >= 2) { - return 2; - } else if ((b & 0xF0) == 0xE0 && l >= 3) { - return 3; - } else if ((b & 0xF8) == 0xF0 && l >= 4) { - return 4; - } - } - return 0; -} - -inline size_t codepoint_count(const char *s8, size_t l) { - size_t count = 0; - for (size_t i = 0; i < l;) { - auto len = codepoint_length(s8 + i, l - i); - if (len == 0) { - // Invalid UTF-8 byte, treat as single byte to avoid infinite loop - len = 1; - } - i += len; - count++; - } - return count; -} - -inline size_t encode_codepoint(char32_t cp, char *buff) { - if (cp < 0x0080) { - buff[0] = static_cast(cp & 0x7F); - return 1; - } else if (cp < 0x0800) { - buff[0] = static_cast(0xC0 | ((cp >> 6) & 0x1F)); - buff[1] = static_cast(0x80 | (cp & 0x3F)); - return 2; - } else if (cp < 0xD800) { - buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (cp & 0x3F)); - return 3; - } else if (cp < 0xE000) { - // D800 - DFFF is invalid... - return 0; - } else if (cp < 0x10000) { - buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (cp & 0x3F)); - return 3; - } else if (cp < 0x110000) { - buff[0] = static_cast(0xF0 | ((cp >> 18) & 0x7)); - buff[1] = static_cast(0x80 | ((cp >> 12) & 0x3F)); - buff[2] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[3] = static_cast(0x80 | (cp & 0x3F)); - return 4; - } - return 0; -} - -inline std::string encode_codepoint(char32_t cp) { - char buff[4]; - auto l = encode_codepoint(cp, buff); - return std::string(buff, l); -} - -inline bool decode_codepoint(const char *s8, size_t l, size_t &bytes, - char32_t &cp) { - if (l) { - auto b = static_cast(s8[0]); - if ((b & 0x80) == 0) { - bytes = 1; - cp = b; - return true; - } else if ((b & 0xE0) == 0xC0) { - if (l >= 2) { - bytes = 2; - cp = ((static_cast(s8[0] & 0x1F)) << 6) | - (static_cast(s8[1] & 0x3F)); - return true; - } - } else if ((b & 0xF0) == 0xE0) { - if (l >= 3) { - bytes = 3; - cp = ((static_cast(s8[0] & 0x0F)) << 12) | - ((static_cast(s8[1] & 0x3F)) << 6) | - (static_cast(s8[2] & 0x3F)); - return true; - } - } else if ((b & 0xF8) == 0xF0) { - if (l >= 4) { - bytes = 4; - cp = ((static_cast(s8[0] & 0x07)) << 18) | - ((static_cast(s8[1] & 0x3F)) << 12) | - ((static_cast(s8[2] & 0x3F)) << 6) | - (static_cast(s8[3] & 0x3F)); - return true; - } - } - } - return false; -} - -inline size_t decode_codepoint(const char *s8, size_t l, char32_t &cp) { - size_t bytes; - if (decode_codepoint(s8, l, bytes, cp)) { return bytes; } - return 0; -} - -inline char32_t decode_codepoint(const char *s8, size_t l) { - char32_t cp = 0; - decode_codepoint(s8, l, cp); - return cp; -} - -inline std::u32string decode(const char *s8, size_t l) { - std::u32string out; - size_t i = 0; - while (i < l) { - auto beg = i++; - while (i < l && (s8[i] & 0xc0) == 0x80) { - i++; - } - out += decode_codepoint(&s8[beg], (i - beg)); - } - return out; -} - -template const char *u8(const T *s) { - return reinterpret_cast(s); -} - -/*----------------------------------------------------------------------------- - * escape_characters - *---------------------------------------------------------------------------*/ - -inline std::string escape_characters(const char *s, size_t n) { - std::string str; - for (size_t i = 0; i < n; i++) { - auto c = s[i]; - switch (c) { - case '\f': str += "\\f"; break; - case '\n': str += "\\n"; break; - case '\r': str += "\\r"; break; - case '\t': str += "\\t"; break; - case '\v': str += "\\v"; break; - default: str += c; break; - } - } - return str; -} - -inline std::string escape_characters(std::string_view sv) { - return escape_characters(sv.data(), sv.size()); -} - -/*----------------------------------------------------------------------------- - * resolve_escape_sequence - *---------------------------------------------------------------------------*/ - -inline bool is_hex(char c, int &v) { - if ('0' <= c && c <= '9') { - v = c - '0'; - return true; - } else if ('a' <= c && c <= 'f') { - v = c - 'a' + 10; - return true; - } else if ('A' <= c && c <= 'F') { - v = c - 'A' + 10; - return true; - } - return false; -} - -inline bool is_digit(char c, int &v) { - if ('0' <= c && c <= '9') { - v = c - '0'; - return true; - } - return false; -} - -inline std::pair parse_hex_number(const char *s, size_t n, - size_t i) { - int ret = 0; - int val; - while (i < n && is_hex(s[i], val)) { - ret = static_cast(ret * 16 + val); - i++; - } - return std::pair(ret, i); -} - -inline std::pair parse_octal_number(const char *s, size_t n, - size_t i) { - int ret = 0; - int val; - while (i < n && is_digit(s[i], val)) { - ret = static_cast(ret * 8 + val); - i++; - } - return std::pair(ret, i); -} - -inline std::string resolve_escape_sequence(const char *s, size_t n) { - std::string r; - r.reserve(n); - - size_t i = 0; - while (i < n) { - auto ch = s[i]; - if (ch == '\\') { - i++; - assert(i < n); - - switch (s[i]) { - case 'f': - r += '\f'; - i++; - break; - case 'n': - r += '\n'; - i++; - break; - case 'r': - r += '\r'; - i++; - break; - case 't': - r += '\t'; - i++; - break; - case 'v': - r += '\v'; - i++; - break; - case '\'': - r += '\''; - i++; - break; - case '"': - r += '"'; - i++; - break; - case '[': - r += '['; - i++; - break; - case ']': - r += ']'; - i++; - break; - case '\\': - r += '\\'; - i++; - break; - case 'x': - case 'u': { - char32_t cp; - std::tie(cp, i) = parse_hex_number(s, n, i + 1); - r += encode_codepoint(cp); - break; - } - default: { - char32_t cp; - std::tie(cp, i) = parse_octal_number(s, n, i); - r += encode_codepoint(cp); - break; - } - } - } else { - r += ch; - i++; - } - } - return r; -} - -/*----------------------------------------------------------------------------- - * token_to_number_ - This function should be removed eventually - *---------------------------------------------------------------------------*/ - -template T token_to_number_(std::string_view sv) { - T n = 0; -#if __has_include() - if constexpr (!std::is_floating_point::value) { - std::from_chars(sv.data(), sv.data() + sv.size(), n); -#else - if constexpr (false) { -#endif - } else { - auto s = std::string(sv); - std::istringstream ss(s); - ss >> n; - } - return n; -} - -inline std::string to_lower(std::string s) { - for (auto &c : s) { - c = static_cast(std::tolower(static_cast(c))); - } - return s; -} - -/*----------------------------------------------------------------------------- - * Trie - *---------------------------------------------------------------------------*/ - -class Trie { -public: - Trie(const std::vector &items, bool ignore_case) - : ignore_case_(ignore_case), items_count_(items.size()) { - size_t id = 0; - for (const auto &item : items) { - const auto &s = ignore_case ? to_lower(item) : item; - if (item.size() > max_len_) { max_len_ = item.size(); } - for (size_t len = 1; len <= item.size(); len++) { - auto last = len == item.size(); - std::string_view sv(s.data(), len); - auto it = dic_.find(sv); - if (it == dic_.end()) { - dic_.emplace(sv, Info{last, last, id}); - } else if (last) { - it->second.match = true; - } else { - it->second.done = false; - } - } - id++; - } - } - - size_t match(const char *text, size_t text_len, size_t &id) const { - auto limit = std::min(text_len, max_len_); - std::string lower_text; - if (ignore_case_) { - lower_text = to_lower(std::string(text, limit)); - text = lower_text.data(); - } - - size_t match_len = 0; - auto done = false; - size_t len = 1; - while (!done && len <= limit) { - std::string_view sv(text, len); - auto it = dic_.find(sv); - if (it == dic_.end()) { - done = true; - } else { - if (it->second.match) { - match_len = len; - id = it->second.id; - } - if (it->second.done) { done = true; } - } - len += 1; - } - return match_len; - } - - size_t size() const { return dic_.size(); } - size_t items_count() const { return items_count_; } - - friend struct ComputeFirstSet; - -private: - struct Info { - bool done; - bool match; - size_t id; - }; - - // TODO: Use unordered_map when heterogeneous lookup is supported in C++20 - // std::unordered_map dic_; - std::map> dic_; - - bool ignore_case_; - size_t items_count_; - size_t max_len_ = 0; -}; - -/*----------------------------------------------------------------------------- - * PEG - *---------------------------------------------------------------------------*/ - -/* - * Line information utility function - */ -inline std::pair line_info(const char *start, const char *cur) { - auto p = start; - auto col_ptr = p; - auto no = 1; - - while (p < cur) { - if (*p == '\n') { - no++; - col_ptr = p + 1; - } - p++; - } - - auto col = codepoint_count(col_ptr, p - col_ptr) + 1; - - return std::pair(no, col); -} - -/* - * String tag - */ -inline constexpr unsigned int str2tag_core(const char *s, size_t l, - unsigned int h) { - return (l == 0) ? h - : str2tag_core(s + 1, l - 1, - (h * 33) ^ static_cast(*s)); -} - -inline constexpr unsigned int str2tag(std::string_view sv) { - return str2tag_core(sv.data(), sv.size(), 0); -} - -namespace udl { - -inline constexpr unsigned int operator""_(const char *s, size_t l) { - return str2tag_core(s, l, 0); -} - -} // namespace udl - -/* - * Semantic values - */ -class Context; - -struct SemanticValues : protected std::vector { - SemanticValues() = default; - SemanticValues(Context *c) : c_(c) {} - - // Input text - const char *path = nullptr; - const char *ss = nullptr; - - // Matched string - std::string_view sv() const { return sv_; } - - // Definition name - const std::string &name() const { return name_; } - - std::vector tags; - - // Line number and column at which the matched string is - std::pair line_info() const; - - // Choice count - size_t choice_count() const { return choice_count_; } - - // Choice number (0 based index) - size_t choice() const { return choice_; } - - // Tokens - std::vector tokens; - - std::string_view token(size_t id = 0) const { - if (tokens.empty()) { return sv_; } - assert(id < tokens.size()); - return tokens[id]; - } - - // Token conversion - std::string token_to_string(size_t id = 0) const { - return std::string(token(id)); - } - - template T token_to_number() const { - return token_to_number_(token()); - } - - // Transform the semantic value vector to another vector - template - std::vector transform(size_t beg = 0, - size_t end = static_cast(-1)) const { - std::vector r; - end = (std::min)(end, size()); - for (size_t i = beg; i < end; i++) { - r.emplace_back(std::any_cast((*this)[i])); - } - return r; - } - - using std::vector::iterator; - using std::vector::const_iterator; - using std::vector::size; - using std::vector::empty; - using std::vector::assign; - using std::vector::begin; - using std::vector::end; - using std::vector::rbegin; - using std::vector::rend; - using std::vector::operator[]; - using std::vector::at; - using std::vector::resize; - using std::vector::front; - using std::vector::back; - using std::vector::push_back; - using std::vector::pop_back; - using std::vector::insert; - using std::vector::erase; - using std::vector::clear; - using std::vector::swap; - using std::vector::emplace; - using std::vector::emplace_back; - -private: - friend class Context; - friend class Dictionary; - friend class Sequence; - friend class PrioritizedChoice; - friend class Repetition; - friend class Holder; - friend class PrecedenceClimbing; - - Context *c_ = nullptr; - std::string_view sv_; - size_t choice_count_ = 0; - size_t choice_ = 0; - std::string name_; -}; - -/* - * Semantic action - */ -template std::any call(F fn, Args &&...args) { - using R = decltype(fn(std::forward(args)...)); - if constexpr (std::is_void::value) { - fn(std::forward(args)...); - return std::any(); - } else if constexpr (std::is_same::type, - std::any>::value) { - return fn(std::forward(args)...); - } else { - return std::any(fn(std::forward(args)...)); - } -} - -template -struct argument_count : argument_count {}; -template -struct argument_count - : std::integral_constant {}; -template -struct argument_count - : std::integral_constant {}; -template -struct argument_count - : std::integral_constant {}; - -class Action { -public: - Action() = default; - Action(Action &&rhs) = default; - template Action(F fn) : fn_(make_adaptor(fn)) {} - template void operator=(F fn) { fn_ = make_adaptor(fn); } - Action &operator=(const Action &rhs) = default; - - operator bool() const { return bool(fn_); } - - std::any operator()(SemanticValues &vs, std::any &dt, - const std::any &predicate_data) const { - return fn_(vs, dt, predicate_data); - } - -private: - using Fty = std::function; - - template Fty make_adaptor(F fn) { - if constexpr (argument_count::value == 1) { - return [fn](auto &vs, auto & /*dt*/, const auto & /*predicate_data*/) { - return call(fn, vs); - }; - } else if constexpr (argument_count::value == 2) { - return [fn](auto &vs, auto &dt, const auto & /*predicate_data*/) { - return call(fn, vs, dt); - }; - } else { - return [fn](auto &vs, auto &dt, const auto &predicate_data) { - return call(fn, vs, dt, predicate_data); - }; - } - } - - Fty fn_; -}; - -class Predicate { -public: - Predicate() = default; - Predicate(Predicate &&rhs) = default; - template Predicate(F fn) : fn_(make_adaptor(fn)) {} - template void operator=(F fn) { fn_ = make_adaptor(fn); } - Predicate &operator=(const Predicate &rhs) = default; - - operator bool() const { return bool(fn_); } - - bool operator()(const SemanticValues &vs, const std::any &dt, - std::string &msg, std::any &predicate_data) const { - return fn_(vs, dt, msg, predicate_data); - } - -private: - using Fty = std::function; - - template Fty make_adaptor(F fn) { - if constexpr (argument_count::value == 3) { - return [fn](const auto &vs, const auto &dt, auto &msg, - auto & /*predicate_data*/) { return fn(vs, dt, msg); }; - } else { - return [fn](const auto &vs, const auto &dt, auto &msg, - auto &predicate_data) { - return fn(vs, dt, msg, predicate_data); - }; - } - } - - Fty fn_; -}; - -/* - * Parse result helper - */ -inline bool success(size_t len) { return len != static_cast(-1); } - -inline bool fail(size_t len) { return len == static_cast(-1); } - -/* - * Log - */ -using Log = std::function; - -/* - * ErrorInfo - */ -class Definition; - -struct ErrorInfo { - const char *error_pos = nullptr; - std::vector> expected_tokens; - const char *message_pos = nullptr; - std::string message; - std::string label; - const char *last_output_pos = nullptr; - bool keep_previous_token = false; - - void clear() { - error_pos = nullptr; - expected_tokens.clear(); - message_pos = nullptr; - message.clear(); - } - - void add(const char *error_literal, const Definition *error_rule) { - for (const auto &[t, r] : expected_tokens) { - if (t == error_literal && r == error_rule) { return; } - } - expected_tokens.emplace_back(error_literal, error_rule); - } - - void output_log(const Log &log, const char *s, size_t n); - -private: - int cast_char(char c) const { return static_cast(c); } - - std::string heuristic_error_token(const char *s, size_t n, - const char *pos) const { - auto len = n - std::distance(s, pos); - if (len) { - size_t i = 0; - auto c = cast_char(pos[i++]); - if (!std::ispunct(c) && !std::isspace(c)) { - while (i < len && !std::ispunct(cast_char(pos[i])) && - !std::isspace(cast_char(pos[i]))) { - i++; - } - } - - size_t count = CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT; - size_t j = 0; - while (count > 0 && j < i) { - j += codepoint_length(&pos[j], i - j); - count--; - } - - return escape_characters(pos, j); - } - return std::string(); - } - - std::string replace_all(std::string str, const std::string &from, - const std::string &to) const { - size_t pos = 0; - while ((pos = str.find(from, pos)) != std::string::npos) { - str.replace(pos, from.length(), to); - pos += to.length(); - } - return str; - } -}; - -/* - * Context - */ -class Ope; - -using TracerEnter = std::function; - -using TracerLeave = std::function; - -using TracerStartOrEnd = std::function; - -class Context { -public: - const char *path; - const char *s; - const size_t l; - - ErrorInfo error_info; - bool recovered = false; - - std::vector> value_stack; - size_t value_stack_size = 0; - - std::vector rule_stack; - std::vector>> args_stack; - - size_t in_token_boundary_count = 0; - - std::shared_ptr whitespaceOpe; - bool in_whitespace = false; - - std::shared_ptr wordOpe; - - std::vector> capture_entries; - - std::vector cut_stack; - - const size_t def_count; - const bool enablePackratParsing; - std::vector cache_registered; - std::vector cache_success; - - std::map, std::tuple> - cache_values; - - // Left recursion support - struct LRMemo { - size_t len = static_cast(-1); - std::any val; - }; - std::map, LRMemo> lr_memo; - - // Rules whose lr_memo was hit during the current parse scope. - // Used to track LR cycle membership. - std::set lr_refs_hit; - - // Rules currently in their seeding/growing phase at a given position. - // Protected from having their lr_memo erased by inner growers. - std::set> lr_active_seeds; - - void clear_packrat_cache(const char *pos, size_t def_id) { - if (!enablePackratParsing) { return; } - auto col = static_cast(pos - s); - auto idx = def_count * col + def_id; - if (idx < cache_registered.size()) { - cache_registered[idx] = false; - cache_success[idx] = false; - } - cache_values.erase(std::make_pair(col, def_id)); - } - - void write_packrat_cache(const char *pos, size_t def_id, size_t len, - const std::any &val) { - if (!enablePackratParsing) { return; } - auto col = pos - s; - auto idx = def_count * static_cast(col) + def_id; - if (idx >= cache_registered.size()) { return; } - cache_registered[idx] = true; - cache_success[idx] = true; - auto key = std::pair(col, def_id); - cache_values[key] = std::pair(len, val); - } - - TracerEnter tracer_enter; - TracerLeave tracer_leave; - std::any trace_data; - const bool verbose_trace; - - Log log; - - Context(const char *path, const char *s, size_t l, size_t def_count, - std::shared_ptr whitespaceOpe, std::shared_ptr wordOpe, - bool enablePackratParsing, TracerEnter tracer_enter, - TracerLeave tracer_leave, std::any trace_data, bool verbose_trace, - Log log) - : path(path), s(s), l(l), whitespaceOpe(whitespaceOpe), wordOpe(wordOpe), - def_count(def_count), enablePackratParsing(enablePackratParsing), - cache_registered(enablePackratParsing ? def_count * (l + 1) : 0), - cache_success(enablePackratParsing ? def_count * (l + 1) : 0), - tracer_enter(tracer_enter), tracer_leave(tracer_leave), - trace_data(trace_data), verbose_trace(verbose_trace), log(log) { - - push_args({}); - } - - ~Context() { - assert(!value_stack_size); - assert(cut_stack.empty()); - } - - Context(const Context &) = delete; - Context(Context &&) = delete; - Context operator=(const Context &) = delete; - - // Per-rule packrat stats (populated when packrat_stats is non-null) - struct PackratStats { - size_t hits = 0; - size_t misses = 0; - }; - std::vector *packrat_stats = nullptr; - - // Per-rule packrat filter: if set, only rules with filter[def_id]=true - // use full memoization (cache_values map). Others use bitvector-only - // re-entry guard. - const std::vector *packrat_rule_filter = nullptr; - - template - void packrat(const char *a_s, size_t def_id, size_t &len, std::any &val, - T fn) { - if (!enablePackratParsing) { - fn(val); - return; - } - - auto col = a_s - s; - auto idx = def_count * static_cast(col) + def_id; - - if (cache_registered[idx]) { - if (packrat_stats && def_id < packrat_stats->size()) { - (*packrat_stats)[def_id].hits++; - } - if (cache_success[idx]) { - auto key = std::pair(col, def_id); - std::tie(len, val) = cache_values[key]; - return; - } else { - len = static_cast(-1); - return; - } - } else { - // Pre-register as failure (re-entry guard for all rules) - cache_registered[idx] = true; - cache_success[idx] = false; - - if (packrat_stats && def_id < packrat_stats->size()) { - (*packrat_stats)[def_id].misses++; - } - - fn(val); - - bool full_memo = - !packrat_rule_filter || (def_id < packrat_rule_filter->size() && - (*packrat_rule_filter)[def_id]); - if (full_memo) { - if (success(len)) { write_packrat_cache(a_s, def_id, len, val); } - } else { - // Guard-only: undo registration so future calls re-parse - cache_registered[idx] = false; - } - return; - } - } - - // Semantic values - SemanticValues &push_semantic_values_scope() { - assert(value_stack_size <= value_stack.size()); - if (value_stack_size == value_stack.size()) { - value_stack.emplace_back(std::make_shared(this)); - } else { - auto &vs = *value_stack[value_stack_size]; - if (!vs.empty()) { - vs.clear(); - if (!vs.tags.empty()) { vs.tags.clear(); } - } - vs.sv_ = std::string_view(); - vs.choice_count_ = 0; - vs.choice_ = 0; - if (!vs.tokens.empty()) { vs.tokens.clear(); } - } - - auto &vs = *value_stack[value_stack_size++]; - vs.path = path; - vs.ss = s; - return vs; - } - - void pop_semantic_values_scope() { value_stack_size--; } - - // Arguments - void push_args(std::vector> &&args) { - args_stack.emplace_back(std::move(args)); - } - - void pop_args() { args_stack.pop_back(); } - - const std::vector> &top_args() const { - return args_stack[args_stack.size() - 1]; - } - - // Snapshot/Rollback - struct Snapshot { - size_t sv_size; - size_t sv_tags_size; - size_t sv_tokens_size; - std::string_view sv_sv; - size_t choice_count; - size_t choice; - size_t capture_size; - }; - - Snapshot snapshot(const SemanticValues &vs) const { - return {vs.size(), vs.tags.size(), vs.tokens.size(), vs.sv_, - vs.choice_count_, vs.choice_, capture_entries.size()}; - } - - void rollback(SemanticValues &vs, const Snapshot &snap) { - vs.resize(snap.sv_size); - vs.tags.resize(snap.sv_tags_size); - vs.tokens.resize(snap.sv_tokens_size); - vs.sv_ = snap.sv_sv; - vs.choice_count_ = snap.choice_count; - vs.choice_ = snap.choice; - capture_entries.resize(snap.capture_size); - } - - // Skip trailing whitespace with trace suppression. - // Returns whitespace length, or -1 on failure. - // No-op (returns 0) if inside a token boundary or no whitespaceOpe. - size_t skip_whitespace(const char *a_s, size_t n, SemanticValues &vs, - std::any &dt); - - // Error - void set_error_pos(const char *a_s, const char *literal = nullptr); - - // Trace - void trace_enter(const Ope &ope, const char *a_s, size_t n, - const SemanticValues &vs, std::any &dt); - void trace_leave(const Ope &ope, const char *a_s, size_t n, - const SemanticValues &vs, std::any &dt, size_t len); - bool is_traceable(const Ope &ope) const; - - // Line info - std::pair line_info(const char *cur) const { - std::call_once(source_line_index_init_, [this]() { - for (size_t pos = 0; pos < l; pos++) { - if (s[pos] == '\n') { source_line_index.push_back(pos); } - } - source_line_index.push_back(l); - }); - - auto pos = static_cast(std::distance(s, cur)); - - auto it = std::lower_bound( - source_line_index.begin(), source_line_index.end(), pos, - [](size_t element, size_t value) { return element < value; }); - - auto id = static_cast(std::distance(source_line_index.begin(), it)); - auto off = pos - (id == 0 ? 0 : source_line_index[id - 1] + 1); - return std::pair(id + 1, off + 1); - } - - size_t next_trace_id = 0; - std::vector trace_ids; - bool ignore_trace_state = false; - mutable std::once_flag source_line_index_init_; - mutable std::vector source_line_index; -}; - -/* - * Parser operators - */ -class Ope { -public: - struct Visitor; - - virtual ~Ope() = default; - size_t parse(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const; - virtual size_t parse_core(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt) const = 0; - virtual void accept(Visitor &v) = 0; - - bool is_token_boundary = false; - bool is_choice_like = false; -}; - -// Keyword-guarded identifier data, heap-allocated only for matching Sequences. -// Avoids bloating all Sequence objects with bitsets and keyword sets. -struct KeywordGuardData { - std::bitset<256> identifier_first; // first char of identifier - std::bitset<256> identifier_rest; // subsequent chars of identifier - std::vector exact_keywords; // single-word keywords (lowercase) - std::vector prefix_keywords; // first word of compound keywords - size_t min_keyword_len = 0; - size_t max_keyword_len = 0; - - static bool matches_any(const std::vector &keywords, - std::string_view input) { - return std::any_of(keywords.begin(), keywords.end(), - [&](const auto &kw) { return kw == input; }); - } -}; - -class Sequence : public Ope { -public: - template - Sequence(const Args &...args) - : opes_{static_cast>(args)...} {} - Sequence(const std::vector> &opes) : opes_(opes) {} - Sequence(std::vector> &&opes) : opes_(std::move(opes)) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - // Keyword-guarded identifier fast path: - // Fuses !ReservedKeyword into scan-then-lookup - if (kw_guard_) { - if (auto result = parse_keyword_guarded(s, n, vs, c, dt)) { - return *result; - } - // nullopt means prefix keyword match — fall through to normal path - } - size_t i = 0; - for (const auto &ope : opes_) { - auto len = ope->parse(s + i, n - i, vs, c, dt); - if (fail(len)) { return len; } - i += len; - } - return i; - } - - void accept(Visitor &v) override; - - std::vector> opes_; - -private: - friend struct SetupFirstSets; - std::unique_ptr kw_guard_; - - // Returns parse result, or nullopt to fall through to normal path - std::optional parse_keyword_guarded(const char *s, size_t n, - SemanticValues &vs, Context &c, - std::any &dt) const { - const auto &kw = *kw_guard_; - if (n < 1 || !kw.identifier_first.test(static_cast(*s))) { - c.set_error_pos(s); - return static_cast(-1); - } - // Scan identifier using bitset - size_t id_len = 1; - while (id_len < n && - kw.identifier_rest.test(static_cast(s[id_len]))) { - id_len++; - } - // Skip keyword matching if identifier length is out of range - if (id_len >= kw.min_keyword_len && id_len <= kw.max_keyword_len) { - char lower_buf[64]; - std::unique_ptr lower_heap; - char *lower = lower_buf; - if (id_len > sizeof(lower_buf)) { - lower_heap.reset(new char[id_len]); - lower = lower_heap.get(); - } - std::transform(s, s + id_len, lower, [](unsigned char ch) { - return static_cast(std::tolower(ch)); - }); - std::string_view lower_sv(lower, id_len); - - if (KeywordGuardData::matches_any(kw.exact_keywords, lower_sv)) { - c.set_error_pos(s); - return static_cast(-1); - } - if (KeywordGuardData::matches_any(kw.prefix_keywords, lower_sv)) { - return std::nullopt; - } - } - // Success: emit token and consume trailing whitespace - vs.tokens.emplace_back(std::string_view(s, id_len)); - auto wl = c.skip_whitespace(s + id_len, n - id_len, vs, dt); - if (fail(wl)) { return wl; } - return id_len + wl; - } -}; - -struct FirstSet { - // First-Set: set of possible first bytes for an expression. - // Used by PrioritizedChoice to skip alternatives that cannot match. - std::bitset<256> chars; // byte values that can appear as the first byte - bool can_be_empty = false; // true if the expression can match empty string - bool any_char = false; // true if any character can appear (cannot filter) - const char *first_literal = nullptr; // first literal for error reporting - const Definition *first_rule = - nullptr; // first token rule for error reporting - - void merge(const FirstSet &other) { - chars |= other.chars; - if (other.can_be_empty) { can_be_empty = true; } - if (other.any_char) { any_char = true; } - // Note: first_literal/first_rule are NOT merged — per-alternative - } -}; - -class PrioritizedChoice : public Ope { -public: - template - PrioritizedChoice(bool for_label, const Args &...args) - : opes_{static_cast>(args)...}, - for_label_(for_label) { - is_choice_like = true; - } - PrioritizedChoice(const std::vector> &opes) - : opes_(opes) { - is_choice_like = true; - } - PrioritizedChoice(std::vector> &&opes) - : opes_(std::move(opes)) { - is_choice_like = true; - } - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - size_t len = static_cast(-1); - - if (!for_label_) { c.cut_stack.push_back(false); } - auto se = scope_exit([&]() { - if (!for_label_) { c.cut_stack.pop_back(); } - }); - - size_t id = 0; - for (const auto &ope : opes_) { - // First-Set filtering: skip if next byte cannot start this alternative - if (n > 0 && id < first_sets_.size()) { - const auto &fs = first_sets_[id]; - if (!fs.any_char && !fs.can_be_empty && - !fs.chars.test(static_cast(*s))) { - if (c.log && (fs.first_literal || fs.first_rule)) { - if (c.error_info.error_pos <= s) { - if (c.error_info.error_pos < s || !(id > 0)) { - c.error_info.error_pos = s; - c.error_info.expected_tokens.clear(); - } - if (fs.first_literal) { - c.error_info.add(fs.first_literal, nullptr); - } else { - c.error_info.add(nullptr, fs.first_rule); - } - } - } - id++; - continue; - } - } - - if (!c.cut_stack.empty()) { c.cut_stack.back() = false; } - - auto snap = c.snapshot(vs); - c.error_info.keep_previous_token = id > 0; - - len = ope->parse(s, n, vs, c, dt); - - if (success(len)) { - vs.choice_count_ = opes_.size(); - vs.choice_ = id; - break; - } - - c.rollback(vs, snap); - - if (!c.cut_stack.empty() && c.cut_stack.back()) { break; } - - id++; - } - - c.error_info.keep_previous_token = false; - return len; - } - - void accept(Visitor &v) override; - - size_t size() const { return opes_.size(); } - - std::vector> opes_; - bool for_label_ = false; - std::vector first_sets_; -}; - -class Repetition : public Ope { -public: - Repetition(const std::shared_ptr &ope, size_t min, size_t max) - : ope_(ope), min_(min), max_(max) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - // ISpan fast path: tight loop for ASCII CharacterClass repetition. - // Safe because each ASCII match is exactly 1 byte, so byte count == match - // count. - if (span_bitset_) { - const auto &bitset = *span_bitset_; - size_t i = 0; - if (max_ == std::numeric_limits::max()) { - // Unbounded repetition (*, +): no per-iteration max check - while (i < n && bitset.test(static_cast(s[i]))) { - i++; - } - } else { - auto limit = std::min(n, max_); - while (i < limit && bitset.test(static_cast(s[i]))) { - i++; - } - } - if (i < min_) { - c.set_error_pos(s + i); - return static_cast(-1); - } - return i; - } - - size_t count = 0; - size_t i = 0; - while (count < min_) { - auto len = ope_->parse(s + i, n - i, vs, c, dt); - if (fail(len)) { return len; } - i += len; - count++; - } - - while (count < max_) { - auto snap = c.snapshot(vs); - auto len = ope_->parse(s + i, n - i, vs, c, dt); - if (fail(len)) { - c.rollback(vs, snap); - break; - } - i += len; - count++; - } - return i; - } - - void accept(Visitor &v) override; - - bool is_zom() const { - return min_ == 0 && max_ == std::numeric_limits::max(); - } - - static std::shared_ptr zom(const std::shared_ptr &ope) { - return std::make_shared(ope, 0, - std::numeric_limits::max()); - } - - static std::shared_ptr oom(const std::shared_ptr &ope) { - return std::make_shared(ope, 1, - std::numeric_limits::max()); - } - - static std::shared_ptr opt(const std::shared_ptr &ope) { - return std::make_shared(ope, 0, 1); - } - - std::shared_ptr ope_; - size_t min_; - size_t max_; - const std::bitset<256> *span_bitset_ = - nullptr; // non-owning, set by SetupFirstSets -}; - -class AndPredicate : public Ope { -public: - AndPredicate(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - auto snap = c.snapshot(vs); - auto len = ope_->parse(s, n, vs, c, dt); - c.rollback(vs, snap); // Always rollback — predicates consume nothing - if (success(len)) { - return 0; - } else { - return len; - } - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class NotPredicate : public Ope { -public: - NotPredicate(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - auto snap = c.snapshot(vs); - auto len = ope_->parse(s, n, vs, c, dt); - c.rollback(vs, snap); // Always rollback — predicates consume nothing - if (success(len)) { - c.set_error_pos(s); - return static_cast(-1); - } else { - return 0; - } - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class Dictionary : public Ope, public std::enable_shared_from_this { -public: - Dictionary(const std::vector &v, bool ignore_case) - : trie_(v, ignore_case) { - is_choice_like = true; - } - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - Trie trie_; -}; - -class LiteralString : public Ope, - public std::enable_shared_from_this { -public: - LiteralString(std::string &&s, bool ignore_case) - : lit_(std::move(s)), ignore_case_(ignore_case), - lower_lit_(ignore_case ? to_lower(lit_) : std::string()), - is_word_(false) {} - - LiteralString(const std::string &s, bool ignore_case) - : lit_(s), ignore_case_(ignore_case), - lower_lit_(ignore_case ? to_lower(lit_) : std::string()), - is_word_(false) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::string lit_; - bool ignore_case_; - std::string lower_lit_; // pre-computed for ignore_case - mutable std::once_flag init_is_word_; - mutable bool is_word_; -}; - -class CharacterClass : public Ope, - public std::enable_shared_from_this { -public: - CharacterClass(const std::string &s, bool negated, bool ignore_case) - : negated_(negated), ignore_case_(ignore_case) { - auto chars = decode(s.data(), s.length()); - auto i = 0u; - while (i < chars.size()) { - if (i + 2 < chars.size() && chars[i + 1] == '-') { - auto cp1 = chars[i]; - auto cp2 = chars[i + 2]; - ranges_.emplace_back(std::pair(cp1, cp2)); - i += 3; - } else { - auto cp = chars[i]; - ranges_.emplace_back(std::pair(cp, cp)); - i += 1; - } - } - assert(!ranges_.empty()); - setup_ascii_bitset(); - } - - CharacterClass(const std::vector> &ranges, - bool negated, bool ignore_case) - : ranges_(ranges), negated_(negated), ignore_case_(ignore_case) { - assert(!ranges_.empty()); - setup_ascii_bitset(); - } - - size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, - Context &c, std::any & /*dt*/) const override { - if (n < 1) { - c.set_error_pos(s); - return static_cast(-1); - } - - char32_t cp = 0; - auto len = decode_codepoint(s, n, cp); - - for (const auto &range : ranges_) { - if (in_range(range, cp)) { - if (negated_) { - c.set_error_pos(s); - return static_cast(-1); - } else { - return len; - } - } - } - - if (negated_) { - return len; - } else { - c.set_error_pos(s); - return static_cast(-1); - } - } - - void accept(Visitor &v) override; - - friend struct ComputeFirstSet; - - bool is_ascii_only() const { return is_ascii_only_; } - const std::bitset<256> &ascii_bitset() const { return ascii_bitset_; } - -private: - bool in_range(const std::pair &range, char32_t cp) const { - if (ignore_case_) { - auto cpl = std::tolower(cp); - return std::tolower(range.first) <= cpl && - cpl <= std::tolower(range.second); - } else { - return range.first <= cp && cp <= range.second; - } - } - - void setup_ascii_bitset() { - if (negated_) { return; } // negated classes can match non-ASCII - for (const auto &[lo, hi] : ranges_) { - if (lo > 0x7F || hi > 0x7F) { return; } - } - is_ascii_only_ = true; - for (const auto &[lo, hi] : ranges_) { - for (auto cp = lo; cp <= hi; cp++) { - auto ch = static_cast(cp); - ascii_bitset_.set(ch); - if (ignore_case_) { - ascii_bitset_.set(static_cast(std::toupper(ch))); - ascii_bitset_.set(static_cast(std::tolower(ch))); - } - } - } - } - - std::vector> ranges_; - bool negated_; - bool ignore_case_; - std::bitset<256> ascii_bitset_; - bool is_ascii_only_ = false; -}; - -class Character : public Ope, public std::enable_shared_from_this { -public: - Character(char32_t ch) : ch_(ch) {} - - size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, - Context &c, std::any & /*dt*/) const override { - if (n < 1) { - c.set_error_pos(s); - return static_cast(-1); - } - - char32_t cp = 0; - auto len = decode_codepoint(s, n, cp); - - if (cp != ch_) { - c.set_error_pos(s); - return static_cast(-1); - } - return len; - } - - void accept(Visitor &v) override; - - char32_t ch_; -}; - -class AnyCharacter : public Ope, - public std::enable_shared_from_this { -public: - size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, - Context &c, std::any & /*dt*/) const override { - auto len = codepoint_length(s, n); - if (len < 1) { - c.set_error_pos(s); - return static_cast(-1); - } - return len; - } - - void accept(Visitor &v) override; -}; - -class CaptureScope : public Ope { -public: - CaptureScope(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - auto cap_snap = c.capture_entries.size(); - auto len = ope_->parse(s, n, vs, c, dt); - c.capture_entries.resize(cap_snap); // Always rollback (isolation) - return len; - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class Capture : public Ope { -public: - using MatchAction = std::function; - - Capture(const std::shared_ptr &ope, MatchAction ma) - : ope_(ope), match_action_(ma) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - auto len = ope_->parse(s, n, vs, c, dt); - if (success(len) && match_action_) { match_action_(s, len, c); } - return len; - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; - MatchAction match_action_; -}; - -class TokenBoundary : public Ope { -public: - TokenBoundary(const std::shared_ptr &ope) : ope_(ope) { - is_token_boundary = true; - } - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class Ignore : public Ope { -public: - Ignore(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, - Context &c, std::any &dt) const override { - auto &chvs = c.push_semantic_values_scope(); - auto se = scope_exit([&]() { c.pop_semantic_values_scope(); }); - return ope_->parse(s, n, chvs, c, dt); - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -using Parser = std::function; - -class User : public Ope { -public: - User(Parser fn) : fn_(fn) {} - size_t parse_core(const char *s, size_t n, SemanticValues &vs, - Context & /*c*/, std::any &dt) const override { - assert(fn_); - return fn_(s, n, vs, dt); - } - void accept(Visitor &v) override; - std::function - fn_; -}; - -class WeakHolder : public Ope { -public: - WeakHolder(const std::shared_ptr &ope) : weak_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - auto ope = weak_.lock(); - assert(ope); - return ope->parse(s, n, vs, c, dt); - } - - void accept(Visitor &v) override; - - std::weak_ptr weak_; -}; - -class Holder : public Ope { -public: - Holder(Definition *outer) : outer_(outer) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::any reduce(SemanticValues &vs, std::any &dt, - const std::any &predicate_data) const; - - const std::string &name() const; - const std::string &trace_name() const; - - std::shared_ptr ope_; - Definition *outer_; - mutable std::once_flag trace_name_init_; - mutable std::string trace_name_; - - friend class Definition; -}; - -using Grammar = std::unordered_map; - -class Reference : public Ope, public std::enable_shared_from_this { -public: - Reference(const Grammar &grammar, const std::string &name, const char *s, - bool is_macro, const std::vector> &args) - : grammar_(grammar), name_(name), s_(s), is_macro_(is_macro), args_(args), - rule_(nullptr), iarg_(0) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::shared_ptr get_core_operator() const; - - const Grammar &grammar_; - const std::string name_; - const char *s_; - - const bool is_macro_; - const std::vector> args_; - - Definition *rule_; - size_t iarg_; -}; - -class Whitespace : public Ope { -public: - Whitespace(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - if (c.in_whitespace) { return 0; } - c.in_whitespace = true; - auto se = scope_exit([&]() { c.in_whitespace = false; }); - return ope_->parse(s, n, vs, c, dt); - } - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class BackReference : public Ope { -public: - BackReference(std::string &&name) : name_(std::move(name)) {} - - BackReference(const std::string &name) : name_(name) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::string name_; -}; - -class PrecedenceClimbing : public Ope { -public: - using BinOpeInfo = std::map>; - - PrecedenceClimbing(const std::shared_ptr &atom, - const std::shared_ptr &binop, const BinOpeInfo &info, - const Definition &rule) - : atom_(atom), binop_(binop), info_(info), rule_(rule) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override { - return parse_expression(s, n, vs, c, dt, 0); - } - - void accept(Visitor &v) override; - - std::shared_ptr atom_; - std::shared_ptr binop_; - BinOpeInfo info_; - const Definition &rule_; - -private: - size_t parse_expression(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt, size_t min_prec) const; - - Definition &get_reference_for_binop(Context &c) const; -}; - -class Recovery : public Ope { -public: - Recovery(const std::shared_ptr &ope) : ope_(ope) {} - - size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, - std::any &dt) const override; - - void accept(Visitor &v) override; - - std::shared_ptr ope_; -}; - -class Cut : public Ope, public std::enable_shared_from_this { -public: - size_t parse_core(const char * /*s*/, size_t /*n*/, SemanticValues & /*vs*/, - Context &c, std::any & /*dt*/) const override { - if (!c.cut_stack.empty()) { c.cut_stack.back() = true; } - return 0; - } - - void accept(Visitor &v) override; -}; - -/* - * Factories - */ -template std::shared_ptr seq(Args &&...args) { - return std::make_shared(static_cast>(args)...); -} - -template std::shared_ptr cho(Args &&...args) { - return std::make_shared( - false, static_cast>(args)...); -} - -template std::shared_ptr cho4label_(Args &&...args) { - return std::make_shared( - true, static_cast>(args)...); -} - -inline std::shared_ptr zom(const std::shared_ptr &ope) { - return Repetition::zom(ope); -} - -inline std::shared_ptr oom(const std::shared_ptr &ope) { - return Repetition::oom(ope); -} - -inline std::shared_ptr opt(const std::shared_ptr &ope) { - return Repetition::opt(ope); -} - -inline std::shared_ptr rep(const std::shared_ptr &ope, size_t min, - size_t max) { - return std::make_shared(ope, min, max); -} - -inline std::shared_ptr apd(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr npd(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr dic(const std::vector &v, - bool ignore_case) { - return std::make_shared(v, ignore_case); -} - -inline std::shared_ptr lit(std::string &&s) { - return std::make_shared(s, false); -} - -inline std::shared_ptr liti(std::string &&s) { - return std::make_shared(s, true); -} - -inline std::shared_ptr cls(const std::string &s) { - return std::make_shared(s, false, false); -} - -inline std::shared_ptr -cls(const std::vector> &ranges, - bool ignore_case = false) { - return std::make_shared(ranges, false, ignore_case); -} - -inline std::shared_ptr ncls(const std::string &s) { - return std::make_shared(s, true, false); -} - -inline std::shared_ptr -ncls(const std::vector> &ranges, - bool ignore_case = false) { - return std::make_shared(ranges, true, ignore_case); -} - -inline std::shared_ptr chr(char32_t dt) { - return std::make_shared(dt); -} - -inline std::shared_ptr dot() { return std::make_shared(); } - -inline std::shared_ptr csc(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr cap(const std::shared_ptr &ope, - Capture::MatchAction ma) { - return std::make_shared(ope, ma); -} - -inline std::shared_ptr tok(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr ign(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr -usr(std::function - fn) { - return std::make_shared(fn); -} - -inline std::shared_ptr ref(const Grammar &grammar, const std::string &name, - const char *s, bool is_macro, - const std::vector> &args) { - return std::make_shared(grammar, name, s, is_macro, args); -} - -inline std::shared_ptr wsp(const std::shared_ptr &ope) { - return std::make_shared(std::make_shared(ope)); -} - -inline std::shared_ptr bkr(std::string &&name) { - return std::make_shared(name); -} - -inline std::shared_ptr pre(const std::shared_ptr &atom, - const std::shared_ptr &binop, - const PrecedenceClimbing::BinOpeInfo &info, - const Definition &rule) { - return std::make_shared(atom, binop, info, rule); -} - -inline std::shared_ptr rec(const std::shared_ptr &ope) { - return std::make_shared(ope); -} - -inline std::shared_ptr cut() { return std::make_shared(); } - -/* - * Visitor - */ -struct Ope::Visitor { - virtual ~Visitor() {} - virtual void visit(Sequence &) {} - virtual void visit(PrioritizedChoice &) {} - virtual void visit(Repetition &) {} - virtual void visit(AndPredicate &) {} - virtual void visit(NotPredicate &) {} - virtual void visit(Dictionary &) {} - virtual void visit(LiteralString &) {} - virtual void visit(CharacterClass &) {} - virtual void visit(Character &) {} - virtual void visit(AnyCharacter &) {} - virtual void visit(CaptureScope &) {} - virtual void visit(Capture &) {} - virtual void visit(TokenBoundary &) {} - virtual void visit(Ignore &) {} - virtual void visit(User &) {} - virtual void visit(WeakHolder &) {} - virtual void visit(Holder &) {} - virtual void visit(Reference &) {} - virtual void visit(Whitespace &) {} - virtual void visit(BackReference &) {} - virtual void visit(PrecedenceClimbing &) {} - virtual void visit(Recovery &) {} - virtual void visit(Cut &) {} -}; - -struct TraversalVisitor : public Ope::Visitor { - using Ope::Visitor::visit; - void visit(Sequence &ope) override { - for (auto &op : ope.opes_) { - op->accept(*this); - } - } - void visit(PrioritizedChoice &ope) override { - for (auto &op : ope.opes_) { - op->accept(*this); - } - } - void visit(Repetition &ope) override { ope.ope_->accept(*this); } - void visit(AndPredicate &ope) override { ope.ope_->accept(*this); } - void visit(NotPredicate &ope) override { ope.ope_->accept(*this); } - void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } - void visit(Capture &ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } - void visit(Ignore &ope) override { ope.ope_->accept(*this); } - void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder &ope) override { ope.ope_->accept(*this); } - void visit(Whitespace &ope) override { ope.ope_->accept(*this); } - void visit(Recovery &ope) override { ope.ope_->accept(*this); } - void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } -}; - -struct TraceOpeName : public Ope::Visitor { - using Ope::Visitor::visit; - - void visit(Sequence &) override { name_ = "Sequence"; } - void visit(PrioritizedChoice &) override { name_ = "PrioritizedChoice"; } - void visit(Repetition &) override { name_ = "Repetition"; } - void visit(AndPredicate &) override { name_ = "AndPredicate"; } - void visit(NotPredicate &) override { name_ = "NotPredicate"; } - void visit(Dictionary &) override { name_ = "Dictionary"; } - void visit(LiteralString &) override { name_ = "LiteralString"; } - void visit(CharacterClass &) override { name_ = "CharacterClass"; } - void visit(Character &) override { name_ = "Character"; } - void visit(AnyCharacter &) override { name_ = "AnyCharacter"; } - void visit(CaptureScope &) override { name_ = "CaptureScope"; } - void visit(Capture &) override { name_ = "Capture"; } - void visit(TokenBoundary &) override { name_ = "TokenBoundary"; } - void visit(Ignore &) override { name_ = "Ignore"; } - void visit(User &) override { name_ = "User"; } - void visit(WeakHolder &) override { name_ = "WeakHolder"; } - void visit(Holder &ope) override { name_ = ope.trace_name().data(); } - void visit(Reference &) override { name_ = "Reference"; } - void visit(Whitespace &) override { name_ = "Whitespace"; } - void visit(BackReference &) override { name_ = "BackReference"; } - void visit(PrecedenceClimbing &) override { name_ = "PrecedenceClimbing"; } - void visit(Recovery &) override { name_ = "Recovery"; } - void visit(Cut &) override { name_ = "Cut"; } - - static std::string get(Ope &ope) { - TraceOpeName vis; - ope.accept(vis); - return vis.name_; - } - -private: - const char *name_ = nullptr; -}; - -struct AssignIDToDefinition : public TraversalVisitor { - using TraversalVisitor::visit; - - void visit(Holder &ope) override; - void visit(Reference &ope) override; - void visit(PrecedenceClimbing &ope) override; - - std::unordered_map ids; -}; - -struct IsLiteralToken : public Ope::Visitor { - using Ope::Visitor::visit; - - void visit(PrioritizedChoice &ope) override { - for (const auto &op : ope.opes_) { - if (!IsLiteralToken::check(*op)) { return; } - } - result_ = true; - } - - void visit(Dictionary &) override { result_ = true; } - void visit(LiteralString &) override { result_ = true; } - - static bool check(Ope &ope) { - IsLiteralToken vis; - ope.accept(vis); - return vis.result_; - } - -private: - bool result_ = false; -}; - -struct TokenChecker : public TraversalVisitor { - using TraversalVisitor::visit; - - void visit(TokenBoundary &) override { has_token_boundary_ = true; } - void visit(AndPredicate &) override {} - void visit(NotPredicate &) override {} - void visit(WeakHolder &) override { has_rule_ = true; } - void visit(Reference &ope) override; - - static bool is_token(Ope &ope) { - if (IsLiteralToken::check(ope)) { return true; } - - TokenChecker vis; - ope.accept(vis); - return vis.has_token_boundary_ || !vis.has_rule_; - } - -private: - bool has_token_boundary_ = false; - bool has_rule_ = false; -}; - -struct FindLiteralToken : public Ope::Visitor { - using Ope::Visitor::visit; - - void visit(LiteralString &ope) override { token_ = ope.lit_.data(); } - void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } - void visit(Ignore &ope) override { ope.ope_->accept(*this); } - void visit(Reference &ope) override; - void visit(Recovery &ope) override { ope.ope_->accept(*this); } - - static const char *token(Ope &ope) { - FindLiteralToken vis; - ope.accept(vis); - return vis.token_; - } - -private: - const char *token_ = nullptr; -}; - -struct DetectLeftRecursion : public TraversalVisitor { - using TraversalVisitor::visit; - - DetectLeftRecursion(const std::string &name) : name_(name) {} - - void visit(Sequence &ope) override { - for (const auto &op : ope.opes_) { - op->accept(*this); - if (done_) { - break; - } else if (error_s) { - done_ = true; - break; - } - } - } - void visit(PrioritizedChoice &ope) override { - for (const auto &op : ope.opes_) { - op->accept(*this); - if (error_s) { - done_ = true; - break; - } - } - } - void visit(Repetition &ope) override { - ope.ope_->accept(*this); - done_ = ope.min_ > 0; - } - void visit(AndPredicate &ope) override { - ope.ope_->accept(*this); - done_ = false; - } - void visit(NotPredicate &ope) override { - ope.ope_->accept(*this); - done_ = false; - } - void visit(Dictionary &) override { done_ = true; } - void visit(LiteralString &ope) override { done_ = !ope.lit_.empty(); } - void visit(CharacterClass &) override { done_ = true; } - void visit(Character &) override { done_ = true; } - void visit(AnyCharacter &) override { done_ = true; } - void visit(User &) override { done_ = true; } - void visit(Reference &ope) override; - void visit(BackReference &) override { done_ = true; } - void visit(Cut &) override { done_ = true; } - - const char *error_s = nullptr; - - std::shared_ptr resolve_macro_arg(size_t iarg) const; - -private: - std::string name_; - std::unordered_set refs_; - bool done_ = false; - std::vector> *> macro_args_stack_; -}; - -struct ComputeCanBeEmpty : public TraversalVisitor { - using TraversalVisitor::visit; - - bool result = false; - - void visit(Sequence &ope) override { - result = std::all_of(ope.opes_.begin(), ope.opes_.end(), [](auto &op) { - ComputeCanBeEmpty vis; - op->accept(vis); - return vis.result; - }); - } - void visit(PrioritizedChoice &ope) override { - result = std::any_of(ope.opes_.begin(), ope.opes_.end(), [](auto &op) { - ComputeCanBeEmpty vis; - op->accept(vis); - return vis.result; - }); - } - void visit(Repetition &ope) override { result = ope.min_ == 0; } - void visit(AndPredicate &) override { result = true; } - void visit(NotPredicate &) override { result = true; } - void visit(Dictionary &) override { result = false; } - void visit(LiteralString &ope) override { result = ope.lit_.empty(); } - void visit(CharacterClass &) override { result = false; } - void visit(Character &) override { result = false; } - void visit(AnyCharacter &) override { result = false; } - void visit(User &) override { result = false; } - void visit(Reference &ope) override; - void visit(BackReference &) override { result = false; } - void visit(Cut &) override { result = false; } -}; - -struct HasEmptyElement : public TraversalVisitor { - using TraversalVisitor::visit; - - HasEmptyElement(std::vector> &refs, - std::unordered_map &has_error_cache) - : refs_(refs), has_error_cache_(has_error_cache) {} - - void visit(Sequence &ope) override; - void visit(PrioritizedChoice &ope) override { - for (const auto &op : ope.opes_) { - op->accept(*this); - if (is_empty) { return; } - } - } - void visit(Repetition &ope) override { - if (ope.min_ == 0) { - set_error(); - } else { - ope.ope_->accept(*this); - } - } - void visit(AndPredicate &) override { set_error(); } - void visit(NotPredicate &) override { set_error(); } - void visit(LiteralString &ope) override { - if (ope.lit_.empty()) { set_error(); } - } - void visit(Reference &ope) override; - - bool is_empty = false; - const char *error_s = nullptr; - std::string error_name; - -private: - void set_error() { - is_empty = true; - tie(error_s, error_name) = refs_.back(); - } - std::vector> &refs_; - std::unordered_map &has_error_cache_; -}; - -struct DetectInfiniteLoop : public TraversalVisitor { - using TraversalVisitor::visit; - - DetectInfiniteLoop(const char *s, const std::string &name, - std::vector> &refs, - std::unordered_map &has_error_cache) - : refs_(refs), has_error_cache_(has_error_cache) { - refs_.emplace_back(s, name); - } - - DetectInfiniteLoop(std::vector> &refs, - std::unordered_map &has_error_cache) - : refs_(refs), has_error_cache_(has_error_cache) {} - - void visit(Sequence &ope) override { - for (const auto &op : ope.opes_) { - op->accept(*this); - if (has_error) { return; } - } - } - void visit(PrioritizedChoice &ope) override { - for (const auto &op : ope.opes_) { - op->accept(*this); - if (has_error) { return; } - } - } - void visit(Repetition &ope) override { - if (ope.max_ == std::numeric_limits::max()) { - HasEmptyElement vis(refs_, has_error_cache_); - ope.ope_->accept(vis); - if (vis.is_empty) { - has_error = true; - error_s = vis.error_s; - error_name = vis.error_name; - } - } else { - ope.ope_->accept(*this); - } - } - void visit(Reference &ope) override; - - bool has_error = false; - const char *error_s = nullptr; - std::string error_name; - -private: - std::vector> &refs_; - std::unordered_map &has_error_cache_; -}; - -struct ReferenceChecker : public TraversalVisitor { - using TraversalVisitor::visit; - - ReferenceChecker(const Grammar &grammar, - const std::vector ¶ms) - : grammar_(grammar), params_(params) {} - - void visit(Reference &ope) override; - - std::unordered_map error_s; - std::unordered_map error_message; - std::unordered_set referenced; - -private: - const Grammar &grammar_; - const std::vector ¶ms_; -}; - -struct LinkReferences : public TraversalVisitor { - using TraversalVisitor::visit; - - LinkReferences(Grammar &grammar, const std::vector ¶ms) - : grammar_(grammar), params_(params) {} - - void visit(Reference &ope) override; - -private: - Grammar &grammar_; - const std::vector ¶ms_; -}; - -struct FindReference : public Ope::Visitor { - using Ope::Visitor::visit; - - FindReference(const std::vector> &args, - const std::vector ¶ms) - : args_(args), params_(params) {} - - void visit(Sequence &ope) override { - std::vector> opes; - for (const auto &o : ope.opes_) { - o->accept(*this); - opes.emplace_back(std::move(found_ope)); - } - found_ope = std::make_shared(opes); - } - void visit(PrioritizedChoice &ope) override { - std::vector> opes; - for (const auto &o : ope.opes_) { - o->accept(*this); - opes.emplace_back(std::move(found_ope)); - } - found_ope = std::make_shared(opes); - } - void visit(Repetition &ope) override { - ope.ope_->accept(*this); - found_ope = rep(found_ope, ope.min_, ope.max_); - } - void visit(AndPredicate &ope) override { - ope.ope_->accept(*this); - found_ope = apd(found_ope); - } - void visit(NotPredicate &ope) override { - ope.ope_->accept(*this); - found_ope = npd(found_ope); - } - void visit(Dictionary &ope) override { found_ope = ope.shared_from_this(); } - void visit(LiteralString &ope) override { - found_ope = ope.shared_from_this(); - } - void visit(CharacterClass &ope) override { - found_ope = ope.shared_from_this(); - } - void visit(Character &ope) override { found_ope = ope.shared_from_this(); } - void visit(AnyCharacter &ope) override { found_ope = ope.shared_from_this(); } - void visit(CaptureScope &ope) override { - ope.ope_->accept(*this); - found_ope = csc(found_ope); - } - void visit(Capture &ope) override { - ope.ope_->accept(*this); - found_ope = cap(found_ope, ope.match_action_); - } - void visit(TokenBoundary &ope) override { - ope.ope_->accept(*this); - found_ope = tok(found_ope); - } - void visit(Ignore &ope) override { - ope.ope_->accept(*this); - found_ope = ign(found_ope); - } - void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder &ope) override { ope.ope_->accept(*this); } - void visit(Reference &ope) override; - void visit(Whitespace &ope) override { - ope.ope_->accept(*this); - found_ope = wsp(found_ope); - } - void visit(PrecedenceClimbing &ope) override { - ope.atom_->accept(*this); - found_ope = csc(found_ope); - } - void visit(Recovery &ope) override { - ope.ope_->accept(*this); - found_ope = rec(found_ope); - } - void visit(Cut &ope) override { found_ope = ope.shared_from_this(); } - - std::shared_ptr found_ope; - -private: - const std::vector> &args_; - const std::vector ¶ms_; -}; - -/* - * First-Set computation - */ -struct ComputeFirstSet : public TraversalVisitor { - using TraversalVisitor::visit; - - void visit(Sequence &ope) override { - for (const auto &op : ope.opes_) { - auto save = result_; - result_ = FirstSet{}; - op->accept(*this); - auto element_fs = result_; - result_ = save; - result_.chars |= element_fs.chars; - if (element_fs.any_char) { result_.any_char = true; } - if (!result_.first_literal) { - result_.first_literal = element_fs.first_literal; - } - if (!result_.first_rule) { result_.first_rule = element_fs.first_rule; } - if (!element_fs.can_be_empty) { return; } - // This element can be empty, continue to next - } - result_.can_be_empty = true; - } - void visit(PrioritizedChoice &ope) override { - auto save = result_; - for (const auto &op : ope.opes_) { - result_ = FirstSet{}; - op->accept(*this); - save.merge(result_); - } - result_ = save; - } - void visit(Repetition &ope) override { - ope.ope_->accept(*this); - if (ope.min_ == 0) { result_.can_be_empty = true; } - } - void visit(AndPredicate &) override { result_.can_be_empty = true; } - void visit(NotPredicate &) override { result_.can_be_empty = true; } - void visit(Dictionary &ope) override { - for (const auto &[key, info] : ope.trie_.dic_) { - if (!key.empty()) { - auto ch = static_cast(key[0]); - result_.chars.set(ch); - if (ope.trie_.ignore_case_) { - result_.chars.set(static_cast(std::toupper(ch))); - result_.chars.set(static_cast(std::tolower(ch))); - } - } - } - } - void visit(LiteralString &ope) override { - if (ope.lit_.empty()) { - result_.can_be_empty = true; - } else { - auto ch = static_cast(ope.lit_[0]); - result_.chars.set(ch); - if (ope.ignore_case_) { - result_.chars.set(static_cast(std::toupper(ch))); - result_.chars.set(static_cast(std::tolower(ch))); - } - if (!result_.first_literal) { result_.first_literal = ope.lit_.c_str(); } - } - } - void visit(CharacterClass &ope) override { - for (const auto &range : ope.ranges_) { - auto cp1 = range.first; - auto cp2 = range.second; - if (cp1 > 0x7F || cp2 > 0x7F) { - // Non-ASCII range: conservative fallback - result_.any_char = true; - return; - } - for (auto cp = cp1; cp <= cp2; cp++) { - auto ch = static_cast(cp); - result_.chars.set(ch); - if (ope.ignore_case_) { - result_.chars.set(static_cast(std::toupper(ch))); - result_.chars.set(static_cast(std::tolower(ch))); - } - } - } - if (ope.negated_) { - result_.chars.flip(); - result_.any_char = true; // negated class can match non-ASCII - } - } - void visit(Character &ope) override { - if (ope.ch_ > 0x7F) { - result_.any_char = true; - } else { - result_.chars.set(static_cast(ope.ch_)); - } - } - void visit(AnyCharacter &) override { result_.any_char = true; } - void visit(User &) override { result_.any_char = true; } - void visit(Reference &ope) override; - void visit(BackReference &) override { result_.any_char = true; } - void visit(Cut &) override { result_.can_be_empty = true; } - - FirstSet result_; - -private: - std::unordered_set refs_; -}; - -struct SetupFirstSets : public TraversalVisitor { - using TraversalVisitor::visit; - - void visit(Sequence &ope) override; - void setup_keyword_guarded_identifier(Sequence &ope); - - void visit(PrioritizedChoice &ope) override { - ope.first_sets_.clear(); - ope.first_sets_.reserve(ope.opes_.size()); - for (const auto &op : ope.opes_) { - ComputeFirstSet cfs; - op->accept(cfs); - ope.first_sets_.push_back(cfs.result_); - } - for (const auto &op : ope.opes_) { - op->accept(*this); - } - } - void visit(Repetition &ope) override { - ope.ope_->accept(*this); - // ISpan optimization: detect Repetition + ASCII CharacterClass - auto cc = dynamic_cast(ope.ope_.get()); - if (cc && cc->is_ascii_only()) { ope.span_bitset_ = &cc->ascii_bitset(); } - } - void visit(Reference &ope) override; - -private: - std::unordered_set refs_; -}; - -/* - * Keywords - */ -static const char *WHITESPACE_DEFINITION_NAME = "%whitespace"; -static const char *WORD_DEFINITION_NAME = "%word"; -static const char *RECOVER_DEFINITION_NAME = "%recover"; - -/* - * Definition - */ -class Definition { -public: - struct Result { - bool ret; - bool recovered; - size_t len; - ErrorInfo error_info; - }; - - Definition() : holder_(std::make_shared(this)) {} - - Definition(const Definition &rhs) : name(rhs.name), holder_(rhs.holder_) { - holder_->outer_ = this; - } - - Definition(const std::shared_ptr &ope) - : holder_(std::make_shared(this)) { - *this <= ope; - } - - operator std::shared_ptr() { - return std::make_shared(holder_); - } - - Definition &operator<=(const std::shared_ptr &ope) { - holder_->ope_ = ope; - return *this; - } - - Result parse(const char *s, size_t n, const char *path = nullptr, - Log log = nullptr) const { - SemanticValues vs; - std::any dt; - return parse_core(s, n, vs, dt, path, log); - } - - Result parse(const char *s, const char *path = nullptr, - Log log = nullptr) const { - auto n = strlen(s); - return parse(s, n, path, log); - } - - Result parse(const char *s, size_t n, std::any &dt, - const char *path = nullptr, Log log = nullptr) const { - SemanticValues vs; - return parse_core(s, n, vs, dt, path, log); - } - - Result parse(const char *s, std::any &dt, const char *path = nullptr, - Log log = nullptr) const { - auto n = strlen(s); - return parse(s, n, dt, path, log); - } - - template - Result parse_and_get_value(const char *s, size_t n, T &val, - const char *path = nullptr, - Log log = nullptr) const { - SemanticValues vs; - std::any dt; - auto r = parse_core(s, n, vs, dt, path, log); - if (r.ret && !vs.empty() && vs.front().has_value()) { - val = std::any_cast(vs[0]); - } - return r; - } - - template - Result parse_and_get_value(const char *s, T &val, const char *path = nullptr, - Log log = nullptr) const { - auto n = strlen(s); - return parse_and_get_value(s, n, val, path, log); - } - - template - Result parse_and_get_value(const char *s, size_t n, std::any &dt, T &val, - const char *path = nullptr, - Log log = nullptr) const { - SemanticValues vs; - auto r = parse_core(s, n, vs, dt, path, log); - if (r.ret && !vs.empty() && vs.front().has_value()) { - val = std::any_cast(vs[0]); - } - return r; - } - - template - Result parse_and_get_value(const char *s, std::any &dt, T &val, - const char *path = nullptr, - Log log = nullptr) const { - auto n = strlen(s); - return parse_and_get_value(s, n, dt, val, path, log); - } - -#if defined(__cpp_lib_char8_t) - Result parse(const char8_t *s, size_t n, const char *path = nullptr, - Log log = nullptr) const { - return parse(reinterpret_cast(s), n, path, log); - } - - Result parse(const char8_t *s, const char *path = nullptr, - Log log = nullptr) const { - return parse(reinterpret_cast(s), path, log); - } - - Result parse(const char8_t *s, size_t n, std::any &dt, - const char *path = nullptr, Log log = nullptr) const { - return parse(reinterpret_cast(s), n, dt, path, log); - } - - Result parse(const char8_t *s, std::any &dt, const char *path = nullptr, - Log log = nullptr) const { - return parse(reinterpret_cast(s), dt, path, log); - } - - template - Result parse_and_get_value(const char8_t *s, size_t n, T &val, - const char *path = nullptr, - Log log = nullptr) const { - return parse_and_get_value(reinterpret_cast(s), n, val, path, - log); - } - - template - Result parse_and_get_value(const char8_t *s, T &val, - const char *path = nullptr, - Log log = nullptr) const { - return parse_and_get_value(reinterpret_cast(s), val, path, - log); - } - - template - Result parse_and_get_value(const char8_t *s, size_t n, std::any &dt, T &val, - const char *path = nullptr, - Log log = nullptr) const { - return parse_and_get_value(reinterpret_cast(s), n, dt, val, - path, log); - } - - template - Result parse_and_get_value(const char8_t *s, std::any &dt, T &val, - const char *path = nullptr, - Log log = nullptr) const { - return parse_and_get_value(reinterpret_cast(s), dt, val, path, - log); - } -#endif - - void operator=(Action a) { action = a; } - - template Definition &operator,(T fn) { - operator=(fn); - return *this; - } - - Definition &operator~() { - ignoreSemanticValue = true; - return *this; - } - - void accept(Ope::Visitor &v) { holder_->accept(v); } - - std::shared_ptr get_core_operator() const { return holder_->ope_; } - - bool is_token() const { - std::call_once(is_token_init_, [this]() { - is_token_ = TokenChecker::is_token(*get_core_operator()); - }); - return is_token_; - } - - std::string name; - const char *s_ = nullptr; - std::pair line_ = {1, 1}; - - Predicate predicate; - - size_t id = 0; - Action action; - std::function - enter; - std::function - leave; - bool ignoreSemanticValue = false; - std::shared_ptr whitespaceOpe; - std::shared_ptr wordOpe; - bool enablePackratParsing = false; - bool is_macro = false; - std::vector params; - bool disable_action = false; - bool is_left_recursive = false; - bool can_be_empty = false; - - TracerEnter tracer_enter; - TracerLeave tracer_leave; - bool verbose_trace = false; - TracerStartOrEnd tracer_start; - TracerStartOrEnd tracer_end; - - std::string error_message; - bool no_ast_opt = false; - - bool eoi_check = true; - - // Per-rule packrat stats (optional, for profiling) - mutable bool collect_packrat_stats = false; - mutable std::vector packrat_stats_; - -private: - friend class Reference; - friend class ParserGenerator; - - Definition &operator=(const Definition &rhs); - Definition &operator=(Definition &&rhs); - - void initialize_definition_ids() const { - std::call_once(definition_ids_init_, [&]() { - AssignIDToDefinition vis; - holder_->accept(vis); - if (whitespaceOpe) { whitespaceOpe->accept(vis); } - if (wordOpe) { wordOpe->accept(vis); } - definition_ids_.swap(vis.ids); - }); - } - - void initialize_packrat_filter() const; - - Result parse_core(const char *s, size_t n, SemanticValues &vs, std::any &dt, - const char *path, Log log) const { - initialize_definition_ids(); - - std::shared_ptr ope = holder_; - - std::any trace_data; - if (tracer_start) { tracer_start(trace_data); } - auto se = scope_exit([&]() { - if (tracer_end) { tracer_end(trace_data); } - }); - - Context c(path, s, n, definition_ids_.size(), whitespaceOpe, wordOpe, - enablePackratParsing, tracer_enter, tracer_leave, trace_data, - verbose_trace, log); - - if (collect_packrat_stats) { - packrat_stats_.resize(definition_ids_.size()); - c.packrat_stats = &packrat_stats_; - } - - if (enablePackratParsing) { - initialize_packrat_filter(); - if (!packrat_filter_.empty()) { - c.packrat_rule_filter = &packrat_filter_; - } - } - - size_t i = 0; - - if (whitespaceOpe) { - auto save_ignore_trace_state = c.ignore_trace_state; - c.ignore_trace_state = !c.verbose_trace; - auto se = - scope_exit([&]() { c.ignore_trace_state = save_ignore_trace_state; }); - - auto len = whitespaceOpe->parse(s, n, vs, c, dt); - if (fail(len)) { return Result{false, c.recovered, i, c.error_info}; } - - i = len; - } - - auto len = ope->parse(s + i, n - i, vs, c, dt); - auto ret = success(len); - if (ret) { - i += len; - if (eoi_check) { - if (i < n) { - if (c.error_info.error_pos - c.s < s + i - c.s) { - c.error_info.message_pos = s + i; - c.error_info.message = "expected end of input"; - } - ret = false; - } - } - } - return Result{ret, c.recovered, i, c.error_info}; - } - - std::shared_ptr holder_; - mutable std::once_flag is_token_init_; - mutable bool is_token_ = false; - mutable std::once_flag assign_id_to_definition_init_; - mutable std::once_flag definition_ids_init_; - mutable std::unordered_map definition_ids_; - mutable std::once_flag packrat_filter_init_; - mutable std::vector packrat_filter_; -}; - -/* - * Implementations - */ - -inline size_t parse_literal(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt, const std::string &lit, - std::once_flag &init_is_word, bool &is_word, - bool ignore_case, const std::string &lower_lit) { - size_t i = 0; - for (; i < lit.size(); i++) { - if (i >= n || - (ignore_case ? (static_cast(std::tolower( - static_cast(s[i]))) != lower_lit[i]) - : (s[i] != lit[i]))) { - c.set_error_pos(s, lit.data()); - return static_cast(-1); - } - } - - // Word check - if (c.wordOpe) { - auto save_ignore_trace_state = c.ignore_trace_state; - c.ignore_trace_state = !c.verbose_trace; - auto se = - scope_exit([&]() { c.ignore_trace_state = save_ignore_trace_state; }); - - std::call_once(init_is_word, [&]() { - SemanticValues dummy_vs; - Context dummy_c(nullptr, c.s, c.l, 0, nullptr, nullptr, false, nullptr, - nullptr, nullptr, false, nullptr); - std::any dummy_dt; - - auto len = - c.wordOpe->parse(lit.data(), lit.size(), dummy_vs, dummy_c, dummy_dt); - is_word = success(len); - }); - - if (is_word) { - SemanticValues dummy_vs; - Context dummy_c(nullptr, c.s, c.l, 0, nullptr, nullptr, false, nullptr, - nullptr, nullptr, false, nullptr); - std::any dummy_dt; - - NotPredicate ope(c.wordOpe); - auto len = ope.parse(s + i, n - i, dummy_vs, dummy_c, dummy_dt); - if (fail(len)) { - c.set_error_pos(s, lit.data()); - return len; - } - i += len; - } - } - - // Skip whitespace - auto wl = c.skip_whitespace(s + i, n - i, vs, dt); - if (fail(wl)) { return wl; } - i += wl; - - return i; -} - -inline std::pair SemanticValues::line_info() const { - assert(c_); - return c_->line_info(sv_.data()); -} - -inline void ErrorInfo::output_log(const Log &log, const char *s, size_t n) { - if (message_pos) { - if (message_pos > last_output_pos) { - last_output_pos = message_pos; - auto line = line_info(s, message_pos); - std::string msg; - if (auto unexpected_token = heuristic_error_token(s, n, message_pos); - !unexpected_token.empty()) { - msg = replace_all(message, "%t", unexpected_token); - - auto unexpected_char = unexpected_token.substr( - 0, - codepoint_length(unexpected_token.data(), unexpected_token.size())); - - msg = replace_all(msg, "%c", unexpected_char); - } else { - msg = message; - } - log(line.first, line.second, msg, label); - } - } else if (error_pos) { - if (error_pos > last_output_pos) { - last_output_pos = error_pos; - auto line = line_info(s, error_pos); - - std::string msg; - if (expected_tokens.empty()) { - msg = "syntax error."; - } else { - msg = "syntax error"; - - // unexpected token - if (auto unexpected_token = heuristic_error_token(s, n, error_pos); - !unexpected_token.empty()) { - msg += ", unexpected '"; - msg += unexpected_token; - msg += "'"; - } - - auto first_item = true; - size_t i = 0; - while (i < expected_tokens.size()) { - auto [error_literal, error_rule] = expected_tokens[i]; - - // Skip rules start with '_' - if (!(error_rule && error_rule->name[0] == '_')) { - msg += (first_item ? ", expecting " : ", "); - if (error_literal) { - msg += "'"; - msg += error_literal; - msg += "'"; - } else { - msg += "<" + error_rule->name + ">"; - if (label.empty()) { label = error_rule->name; } - } - first_item = false; - } - - i++; - } - msg += "."; - } - log(line.first, line.second, msg, label); - } - } -} - -inline size_t Context::skip_whitespace(const char *a_s, size_t n, - SemanticValues &vs, std::any &dt) { - if (in_token_boundary_count || !whitespaceOpe) { return 0; } - auto save = ignore_trace_state; - ignore_trace_state = !verbose_trace; - auto se = scope_exit([&]() { ignore_trace_state = save; }); - return whitespaceOpe->parse(a_s, n, vs, *this, dt); -} - -inline void Context::set_error_pos(const char *a_s, const char *literal) { - if (log) { - if (error_info.error_pos <= a_s) { - if (error_info.error_pos < a_s || !error_info.keep_previous_token) { - error_info.error_pos = a_s; - error_info.expected_tokens.clear(); - } - - const char *error_literal = nullptr; - const Definition *error_rule = nullptr; - - if (literal) { - error_literal = literal; - } else if (!rule_stack.empty()) { - auto rule = rule_stack.back(); - auto ope = rule->get_core_operator(); - if (auto token = FindLiteralToken::token(*ope); - token && token[0] != '\0') { - error_literal = token; - } - } - - for (auto r : rule_stack) { - error_rule = r; - if (r->is_token()) { break; } - } - - if (error_literal || error_rule) { - error_info.add(error_literal, error_rule); - } - } - } -} - -inline void Context::trace_enter(const Ope &ope, const char *a_s, size_t n, - const SemanticValues &vs, std::any &dt) { - trace_ids.push_back(next_trace_id++); - tracer_enter(ope, a_s, n, vs, *this, dt, trace_data); -} - -inline void Context::trace_leave(const Ope &ope, const char *a_s, size_t n, - const SemanticValues &vs, std::any &dt, - size_t len) { - tracer_leave(ope, a_s, n, vs, *this, dt, len, trace_data); - trace_ids.pop_back(); -} - -inline bool Context::is_traceable(const Ope &ope) const { - if (tracer_enter && tracer_leave) { - if (ignore_trace_state) { return false; } - return !dynamic_cast(&ope); - } - return false; -} - -inline size_t Ope::parse(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt) const { - if (c.is_traceable(*this)) { - c.trace_enter(*this, s, n, vs, dt); - auto len = parse_core(s, n, vs, c, dt); - c.trace_leave(*this, s, n, vs, dt, len); - return len; - } - return parse_core(s, n, vs, c, dt); -} - -inline size_t Dictionary::parse_core(const char *s, size_t n, - SemanticValues &vs, Context &c, - std::any &dt) const { - size_t id; - auto i = trie_.match(s, n, id); - - if (i == 0) { - c.set_error_pos(s); - return static_cast(-1); - } - - vs.choice_count_ = trie_.items_count(); - vs.choice_ = id; - - // Word check - if (c.wordOpe) { - auto save_ignore_trace_state = c.ignore_trace_state; - c.ignore_trace_state = !c.verbose_trace; - auto se = - scope_exit([&]() { c.ignore_trace_state = save_ignore_trace_state; }); - - { - SemanticValues dummy_vs; - Context dummy_c(nullptr, c.s, c.l, 0, nullptr, nullptr, false, nullptr, - nullptr, nullptr, false, nullptr); - std::any dummy_dt; - - NotPredicate ope(c.wordOpe); - auto len = ope.parse(s + i, n - i, dummy_vs, dummy_c, dummy_dt); - if (fail(len)) { - c.set_error_pos(s); - return len; - } - i += len; - } - } - - // Skip whitespace - auto wl = c.skip_whitespace(s + i, n - i, vs, dt); - if (fail(wl)) { return wl; } - i += wl; - - return i; -} - -inline size_t LiteralString::parse_core(const char *s, size_t n, - SemanticValues &vs, Context &c, - std::any &dt) const { - return parse_literal(s, n, vs, c, dt, lit_, init_is_word_, is_word_, - ignore_case_, lower_lit_); -} - -inline size_t TokenBoundary::parse_core(const char *s, size_t n, - SemanticValues &vs, Context &c, - std::any &dt) const { - auto save_ignore_trace_state = c.ignore_trace_state; - c.ignore_trace_state = !c.verbose_trace; - auto se = - scope_exit([&]() { c.ignore_trace_state = save_ignore_trace_state; }); - - size_t len; - { - c.in_token_boundary_count++; - auto se = scope_exit([&]() { c.in_token_boundary_count--; }); - len = ope_->parse(s, n, vs, c, dt); - } - - if (success(len)) { - vs.tokens.emplace_back(std::string_view(s, len)); - - auto wl = c.skip_whitespace(s + len, n - len, vs, dt); - if (fail(wl)) { return wl; } - len += wl; - } - return len; -} - -inline size_t Holder::parse_core(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt) const { - if (!ope_) { - throw std::logic_error("Uninitialized definition ope was used..."); - } - - // Macro reference - if (outer_->is_macro) { - c.rule_stack.push_back(outer_); - auto len = ope_->parse(s, n, vs, c, dt); - c.rule_stack.pop_back(); - return len; - } - - size_t len; - std::any val; - - // Shared parse body: invokes enter/leave callbacks, parses the rule's - // operator, handles actions/predicates/errors, and calls reduce. - // Returns {parse_len, parse_val}. - auto do_parse = [&]() { - size_t parse_len; - std::any parse_val; - - if (outer_->enter) { outer_->enter(c, s, n, dt); } - auto &chvs = c.push_semantic_values_scope(); - auto se = scope_exit([&]() { - c.pop_semantic_values_scope(); - if (outer_->leave) { outer_->leave(c, s, n, parse_len, parse_val, dt); } - }); - - c.rule_stack.push_back(outer_); - parse_len = ope_->parse(s, n, chvs, c, dt); - c.rule_stack.pop_back(); - - if (success(parse_len)) { - chvs.sv_ = std::string_view(s, parse_len); - chvs.name_ = outer_->name; - - auto ope_ptr = ope_.get(); - if (ope_ptr->is_token_boundary) { - ope_ptr = static_cast(ope_ptr)->ope_.get(); - } - if (!ope_ptr->is_choice_like) { - chvs.choice_count_ = 0; - chvs.choice_ = 0; - } - - std::string msg; - std::any predicate_data; - if (outer_->predicate) { - if (!outer_->predicate(chvs, dt, msg, predicate_data)) { - if (c.log && !msg.empty() && c.error_info.message_pos < s) { - c.error_info.message_pos = s; - c.error_info.message = msg; - c.error_info.label = outer_->name; - } - parse_len = static_cast(-1); - } - } - - if (success(parse_len)) { - if (!c.recovered) { parse_val = reduce(chvs, dt, predicate_data); } - } else { - if (c.log && !msg.empty() && c.error_info.message_pos < s) { - c.error_info.message_pos = s; - c.error_info.message = msg; - c.error_info.label = outer_->name; - } - } - } else { - if (c.log && !outer_->error_message.empty() && - c.error_info.message_pos < s) { - c.error_info.message_pos = s; - c.error_info.message = outer_->error_message; - c.error_info.label = outer_->name; - } - } - - return std::make_pair(parse_len, std::move(parse_val)); - }; - - if (outer_->is_left_recursive) { - auto lr_key = std::make_pair(outer_, s); - - // Check LR memo first - auto it = c.lr_memo.find(lr_key); - if (it != c.lr_memo.end()) { - if (success(it->second.len)) { - len = it->second.len; - val = it->second.val; - } else { - len = static_cast(-1); - } - // Record that this rule's lr_memo was accessed. - // Any LR rule currently seeding will know we're in its cycle. - c.lr_refs_hit.insert(outer_); - } else { - // Seed with FAIL - c.lr_memo[lr_key] = {static_cast(-1), {}}; - - // Mark as active seed (protects our lr_memo from inner growers) - c.lr_active_seeds.insert(lr_key); - auto seed_guard = scope_exit([&]() { c.lr_active_seeds.erase(lr_key); }); - - // Track which LR rules are referenced during our parse - // to identify cycle members - auto saved_refs = std::move(c.lr_refs_hit); - c.lr_refs_hit.clear(); - - // Initial parse (self-references will hit the FAIL seed) - auto [initial_len, initial_val] = do_parse(); - - // Rules whose lr_memo was hit during our parse are in our cycle. - // If we detected cycle members, we ourselves are also part of - // the cycle, so add self — this lets parent seeders see us as - // a transitive cycle member. - auto cycle_rules = c.lr_refs_hit; - if (!cycle_rules.empty()) { cycle_rules.insert(outer_); } - - // Restore parent's refs and propagate cycle info upward - c.lr_refs_hit = std::move(saved_refs); - c.lr_refs_hit.insert(cycle_rules.begin(), cycle_rules.end()); - - if (!success(initial_len)) { - // Keep FAIL in lr_memo so we don't re-seed - len = static_cast(-1); - } else { - // Got initial seed, now grow - len = initial_len; - val = std::move(initial_val); - c.lr_memo[lr_key] = {len, val}; - - while (true) { - // Clear this rule's packrat cache - c.clear_packrat_cache(s, outer_->id); - - // Clear lr_memo for cycle-dependent rules at this position, - // but NOT for rules currently in their own seeding phase - // (lr_active_seeds) — those are outer growers we must not - // interfere with. - for (auto memo_it = c.lr_memo.begin(); memo_it != c.lr_memo.end();) { - if (memo_it->first.second == s && memo_it->first.first != outer_ && - cycle_rules.count(memo_it->first.first) && - !c.lr_active_seeds.count(memo_it->first)) { - memo_it = c.lr_memo.erase(memo_it); - } else { - ++memo_it; - } - } - - auto [new_len, new_val] = do_parse(); - - if (!success(new_len) || new_len <= len) { - break; // No improvement, done growing - } - - len = new_len; - val = std::move(new_val); - c.lr_memo[lr_key] = {len, val}; - } - } - - // Write final result to packrat cache (lr_memo entry is kept as - // the primary lookup for LR rules at this position) - if (success(len)) { c.write_packrat_cache(s, outer_->id, len, val); } - } - } else { - if (c.enablePackratParsing) { - // Packrat cache acts as re-entry guard (pre-registered as - // failure before fn is called). - c.packrat(s, outer_->id, len, val, [&](std::any &a_val) { - auto [parse_len, parse_val] = do_parse(); - len = parse_len; - if (success(len)) { a_val = std::move(parse_val); } - }); - } else { - // Without packrat, use lr_memo as re-entry guard to prevent - // stack overflow from undetected left recursion. - auto guard_key = std::make_pair(outer_, s); - if (c.lr_memo.count(guard_key)) { - len = static_cast(-1); - } else { - c.lr_memo[guard_key] = {static_cast(-1), {}}; - auto [parse_len, parse_val] = do_parse(); - len = parse_len; - val = std::move(parse_val); - c.lr_memo.erase(guard_key); - } - } - } - - if (success(len)) { - if (!outer_->ignoreSemanticValue) { - vs.emplace_back(std::move(val)); - vs.tags.emplace_back(str2tag(outer_->name)); - } - } - - return len; -} - -inline std::any Holder::reduce(SemanticValues &vs, std::any &dt, - const std::any &predicate_data) const { - if (outer_->action && !outer_->disable_action) { - return outer_->action(vs, dt, predicate_data); - } else if (vs.empty()) { - return std::any(); - } else { - return std::move(vs.front()); - } -} - -inline const std::string &Holder::name() const { return outer_->name; } - -inline const std::string &Holder::trace_name() const { - std::call_once(trace_name_init_, - [this]() { trace_name_ = "[" + outer_->name + "]"; }); - return trace_name_; -} - -inline size_t Reference::parse_core(const char *s, size_t n, SemanticValues &vs, - Context &c, std::any &dt) const { - auto save_ignore_trace_state = c.ignore_trace_state; - if (rule_ && rule_->ignoreSemanticValue) { - c.ignore_trace_state = !c.verbose_trace; - } - auto se = - scope_exit([&]() { c.ignore_trace_state = save_ignore_trace_state; }); - - if (rule_) { - // Reference rule - if (rule_->is_macro) { - // Macro - FindReference vis(c.top_args(), c.rule_stack.back()->params); - - // Collect arguments - std::vector> args; - for (const auto &arg : args_) { - arg->accept(vis); - args.emplace_back(std::move(vis.found_ope)); - } - - c.push_args(std::move(args)); - auto se = scope_exit([&]() { c.pop_args(); }); - return rule_->holder_->parse(s, n, vs, c, dt); - } else { - // Definition - c.push_args(std::vector>()); - auto se2 = scope_exit([&]() { c.pop_args(); }); - return rule_->holder_->parse(s, n, vs, c, dt); - } - } else { - // Reference parameter in macro - const auto &args = c.top_args(); - return args[iarg_]->parse(s, n, vs, c, dt); - } -} - -inline std::shared_ptr Reference::get_core_operator() const { - return rule_->holder_; -} - -inline size_t BackReference::parse_core(const char *s, size_t n, - SemanticValues &vs, Context &c, - std::any &dt) const { - for (auto it = c.capture_entries.rbegin(); it != c.capture_entries.rend(); - ++it) { - if (it->first == name_) { - const auto &lit = it->second; - std::once_flag init_is_word; - auto is_word = false; - static const std::string empty; - return parse_literal(s, n, vs, c, dt, lit, init_is_word, is_word, false, - empty); - } - } - - c.error_info.message_pos = s; - c.error_info.message = "undefined back reference '$" + name_ + "'..."; - return static_cast(-1); -} - -inline Definition & -PrecedenceClimbing::get_reference_for_binop(Context &c) const { - if (rule_.is_macro) { - // Reference parameter in macro - const auto &args = c.top_args(); - auto iarg = dynamic_cast(*binop_).iarg_; - auto arg = args[iarg]; - return *dynamic_cast(*arg).rule_; - } - - return *dynamic_cast(*binop_).rule_; -} - -inline size_t PrecedenceClimbing::parse_expression(const char *s, size_t n, - SemanticValues &vs, - Context &c, std::any &dt, - size_t min_prec) const { - auto len = atom_->parse(s, n, vs, c, dt); - if (fail(len)) { return len; } - - std::string tok; - auto &rule = get_reference_for_binop(c); - auto action = std::move(rule.action); - - rule.action = [&](SemanticValues &vs2, std::any &dt2, - const std::any &predicate_data2) { - tok = vs2.token(); - if (action) { - return action(vs2, dt2, predicate_data2); - } else if (!vs2.empty()) { - return vs2[0]; - } - return std::any(); - }; - auto action_se = scope_exit([&]() { rule.action = std::move(action); }); - - auto i = len; - while (i < n) { - std::vector save_values(vs.begin(), vs.end()); - auto save_tokens = vs.tokens; - - auto chvs = c.push_semantic_values_scope(); - auto chlen = binop_->parse(s + i, n - i, chvs, c, dt); - c.pop_semantic_values_scope(); - - if (fail(chlen)) { break; } - - auto it = info_.find(tok); - if (it == info_.end()) { break; } - - auto level = std::get<0>(it->second); - auto assoc = std::get<1>(it->second); - - if (level < min_prec) { break; } - - vs.emplace_back(std::move(chvs[0])); - i += chlen; - - auto next_min_prec = level; - if (assoc == 'L') { next_min_prec = level + 1; } - - chvs = c.push_semantic_values_scope(); - chlen = parse_expression(s + i, n - i, chvs, c, dt, next_min_prec); - c.pop_semantic_values_scope(); - - if (fail(chlen)) { - vs.assign(save_values.begin(), save_values.end()); - vs.tokens = save_tokens; - i = chlen; - break; - } - - vs.emplace_back(std::move(chvs[0])); - i += chlen; - - std::any val; - if (rule_.action) { - vs.sv_ = std::string_view(s, i); - static const std::any empty_predicate_data; - val = rule_.action(vs, dt, empty_predicate_data); - } else if (!vs.empty()) { - val = vs[0]; - } - vs.clear(); - vs.emplace_back(std::move(val)); - } - - return i; -} - -inline size_t Recovery::parse_core(const char *s, size_t n, - SemanticValues & /*vs*/, Context &c, - std::any & /*dt*/) const { - const auto &rule = dynamic_cast(*ope_); - - // Custom error message - if (c.log) { - auto label = dynamic_cast(rule.args_[0].get()); - if (label && !label->rule_->error_message.empty()) { - c.error_info.message_pos = s; - c.error_info.message = label->rule_->error_message; - c.error_info.label = label->rule_->name; - } - } - - // Recovery - auto len = static_cast(-1); - { - auto save_log = c.log; - c.log = nullptr; - auto se = scope_exit([&]() { c.log = save_log; }); - - SemanticValues dummy_vs; - std::any dummy_dt; - - len = rule.parse(s, n, dummy_vs, c, dummy_dt); - } - - if (success(len)) { - c.recovered = true; - - if (c.log) { - c.error_info.output_log(c.log, c.s, c.l); - c.error_info.clear(); - } - } - - // Cut - if (!c.cut_stack.empty()) { - c.cut_stack.back() = true; - - if (c.cut_stack.size() == 1) { - // TODO: Remove unneeded entries in packrat memoise table - } - } - - return len; -} - -inline void Sequence::accept(Visitor &v) { v.visit(*this); } -inline void PrioritizedChoice::accept(Visitor &v) { v.visit(*this); } -inline void Repetition::accept(Visitor &v) { v.visit(*this); } -inline void AndPredicate::accept(Visitor &v) { v.visit(*this); } -inline void NotPredicate::accept(Visitor &v) { v.visit(*this); } -inline void Dictionary::accept(Visitor &v) { v.visit(*this); } -inline void LiteralString::accept(Visitor &v) { v.visit(*this); } -inline void CharacterClass::accept(Visitor &v) { v.visit(*this); } -inline void Character::accept(Visitor &v) { v.visit(*this); } -inline void AnyCharacter::accept(Visitor &v) { v.visit(*this); } -inline void CaptureScope::accept(Visitor &v) { v.visit(*this); } -inline void Capture::accept(Visitor &v) { v.visit(*this); } -inline void TokenBoundary::accept(Visitor &v) { v.visit(*this); } -inline void Ignore::accept(Visitor &v) { v.visit(*this); } -inline void User::accept(Visitor &v) { v.visit(*this); } -inline void WeakHolder::accept(Visitor &v) { v.visit(*this); } -inline void Holder::accept(Visitor &v) { v.visit(*this); } -inline void Reference::accept(Visitor &v) { v.visit(*this); } -inline void Whitespace::accept(Visitor &v) { v.visit(*this); } -inline void BackReference::accept(Visitor &v) { v.visit(*this); } -inline void PrecedenceClimbing::accept(Visitor &v) { v.visit(*this); } -inline void Recovery::accept(Visitor &v) { v.visit(*this); } -inline void Cut::accept(Visitor &v) { v.visit(*this); } - -inline void AssignIDToDefinition::visit(Holder &ope) { - auto p = static_cast(ope.outer_); - if (ids.count(p)) { return; } - auto id = ids.size(); - ids[p] = id; - ope.outer_->id = id; - ope.ope_->accept(*this); -} - -inline void AssignIDToDefinition::visit(Reference &ope) { - if (ope.rule_) { - for (const auto &arg : ope.args_) { - arg->accept(*this); - } - ope.rule_->accept(*this); - } -} - -inline void AssignIDToDefinition::visit(PrecedenceClimbing &ope) { - ope.atom_->accept(*this); - ope.binop_->accept(*this); -} - -inline void TokenChecker::visit(Reference &ope) { - if (ope.is_macro_) { - for (const auto &arg : ope.args_) { - arg->accept(*this); - } - } else { - has_rule_ = true; - } -} - -inline void FindLiteralToken::visit(Reference &ope) { - if (ope.is_macro_) { - ope.rule_->accept(*this); - for (const auto &arg : ope.args_) { - arg->accept(*this); - } - } -} - -inline void ComputeCanBeEmpty::visit(Reference &ope) { - result = ope.rule_ && ope.rule_->can_be_empty; -} - -inline void DetectLeftRecursion::visit(Reference &ope) { - if (ope.name_ == name_) { - error_s = ope.s_; - } else if (!ope.rule_ && !macro_args_stack_.empty()) { - // Macro parameter reference: resolve through nested macro arg - // stacks (e.g. B(X) <- C(X) where X is itself a param ref). - auto resolved = resolve_macro_arg(ope.iarg_); - if (resolved) { - resolved->accept(*this); - if (done_ == false) { return; } - } - } else if (!refs_.count(ope.name_)) { - refs_.insert(ope.name_); - if (ope.rule_) { - if (ope.is_macro_) { macro_args_stack_.push_back(&ope.args_); } - ope.rule_->accept(*this); - if (ope.is_macro_) { macro_args_stack_.pop_back(); } - if (done_ == false) { return; } - } - } - // If the referenced rule can match empty, don't mark as done — - // the sequence may continue past this element to find LR. - if (!ope.rule_ && !macro_args_stack_.empty()) { - auto resolved = resolve_macro_arg(ope.iarg_); - if (resolved) { - ComputeCanBeEmpty cbe; - resolved->accept(cbe); - done_ = !cbe.result; - } else { - done_ = true; - } - } else { - done_ = !(ope.rule_ && ope.rule_->can_be_empty); - } -} - -inline std::shared_ptr -DetectLeftRecursion::resolve_macro_arg(size_t iarg) const { - for (int i = static_cast(macro_args_stack_.size()) - 1; i >= 0; i--) { - auto &args = *macro_args_stack_[i]; - if (iarg >= args.size()) { return nullptr; } - auto ref = dynamic_cast(args[iarg].get()); - if (ref && !ref->rule_) { - // Another param ref — resolve using parent level's args - iarg = ref->iarg_; - continue; - } - return args[iarg]; - } - return nullptr; -} - -inline void HasEmptyElement::visit(Sequence &ope) { - auto save_is_empty = false; - const char *save_error_s = nullptr; - std::string save_error_name; - - auto it = ope.opes_.begin(); - while (it != ope.opes_.end()) { - (*it)->accept(*this); - if (!is_empty) { - ++it; - while (it != ope.opes_.end()) { - DetectInfiniteLoop vis(refs_, has_error_cache_); - (*it)->accept(vis); - if (vis.has_error) { - is_empty = true; - error_s = vis.error_s; - error_name = vis.error_name; - } - ++it; - } - return; - } - - save_is_empty = is_empty; - save_error_s = error_s; - save_error_name = error_name; - - is_empty = false; - error_name.clear(); - ++it; - } - - is_empty = save_is_empty; - error_s = save_error_s; - error_name = save_error_name; -} - -inline void HasEmptyElement::visit(Reference &ope) { - auto it = std::find_if(refs_.begin(), refs_.end(), - [&](const std::pair &ref) { - return ope.name_ == ref.second; - }); - if (it != refs_.end()) { return; } - - if (ope.rule_) { - refs_.emplace_back(ope.s_, ope.name_); - ope.rule_->accept(*this); - refs_.pop_back(); - } -} - -inline void DetectInfiniteLoop::visit(Reference &ope) { - auto it = std::find_if(refs_.begin(), refs_.end(), - [&](const std::pair &ref) { - return ope.name_ == ref.second; - }); - if (it != refs_.end()) { return; } - - if (ope.rule_) { - auto it = has_error_cache_.find(ope.name_); - if (it != has_error_cache_.end()) { - has_error = it->second; - } else { - refs_.emplace_back(ope.s_, ope.name_); - ope.rule_->accept(*this); - refs_.pop_back(); - has_error_cache_[ope.name_] = has_error; - } - } - - if (ope.is_macro_) { - for (const auto &arg : ope.args_) { - arg->accept(*this); - } - } -} - -inline void ReferenceChecker::visit(Reference &ope) { - auto it = std::find(params_.begin(), params_.end(), ope.name_); - if (it != params_.end()) { return; } - - if (!grammar_.count(ope.name_)) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "'" + ope.name_ + "' is not defined."; - } else { - if (!referenced.count(ope.name_)) { referenced.insert(ope.name_); } - const auto &rule = grammar_.at(ope.name_); - if (rule.is_macro) { - if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "incorrect number of arguments."; - } - } else if (ope.is_macro_) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "'" + ope.name_ + "' is not macro."; - } - for (const auto &arg : ope.args_) { - arg->accept(*this); - } - } -} - -inline void ComputeFirstSet::visit(Reference &ope) { - if (!ope.rule_) { - // Macro parameter reference — can't predict what it will match - result_.any_char = true; - return; - } - if (refs_.count(ope.name_)) { return; } - refs_.insert(ope.name_); - ope.rule_->accept(*this); - if (!result_.first_rule && ope.rule_->is_token()) { - result_.first_rule = ope.rule_; - } - refs_.erase(ope.name_); -} - -inline void SetupFirstSets::visit(Reference &ope) { - if (!ope.rule_ || refs_.count(ope.name_)) { return; } - refs_.insert(ope.name_); - ope.rule_->accept(*this); - refs_.erase(ope.name_); -} - -inline void SetupFirstSets::visit(Sequence &ope) { - ope.kw_guard_.reset(); - setup_keyword_guarded_identifier(ope); - for (const auto &op : ope.opes_) { - op->accept(*this); - } -} - -inline void SetupFirstSets::setup_keyword_guarded_identifier(Sequence &seq) { - // Detect pattern: NotPredicate(Reference→PrioritizedChoice) - // TokenBoundary(Sequence[CharacterClass, - // Repetition(CharacterClass)]) - // This is the pattern used by: PlainIdentifier <- !ReservedKeyword - // <[a-z_]i[a-z0-9_]i*> - if (seq.opes_.size() != 2) { return; } - - // Child 0 must be NotPredicate - auto *not_pred = dynamic_cast(seq.opes_[0].get()); - if (!not_pred) { return; } - - // NotPredicate's child must be Reference to a rule - auto *ref = dynamic_cast(not_pred->ope_.get()); - if (!ref || !ref->rule_) { return; } - - // The referenced rule's inner operator (Holder) must contain - // PrioritizedChoice - auto *holder = dynamic_cast(ref->get_core_operator().get()); - if (!holder) { return; } - auto *choice = dynamic_cast(holder->ope_.get()); - if (!choice) { return; } - - // Extract keywords from PrioritizedChoice alternatives - std::vector exact_keywords; - std::vector prefix_keywords; - - for (const auto &alt : choice->opes_) { - auto *lit = dynamic_cast(alt.get()); - if (lit) { - if (!lit->ignore_case_) { return; } - exact_keywords.push_back(to_lower(lit->lit_)); - continue; - } - // Check for compound keyword (Sequence of LiteralStrings) - auto *sub_seq = dynamic_cast(alt.get()); - if (sub_seq && !sub_seq->opes_.empty()) { - auto *first_lit = dynamic_cast(sub_seq->opes_[0].get()); - if (first_lit) { - auto all_ignore_case_lits = - std::all_of(sub_seq->opes_.begin(), sub_seq->opes_.end(), - [](const auto &child) { - auto *l = dynamic_cast(child.get()); - return l && l->ignore_case_; - }); - if (all_ignore_case_lits) { - prefix_keywords.push_back(to_lower(first_lit->lit_)); - continue; - } - } - } - // Unrecognized alternative — bail out - return; - } - - if (exact_keywords.empty()) { return; } - - // Child 1 must be TokenBoundary - auto *tb = dynamic_cast(seq.opes_[1].get()); - if (!tb) { return; } - - // TokenBoundary content: Sequence[CharacterClass, Repetition(CharacterClass)] - // or just CharacterClass (single char identifier) - CharacterClass *first_cc = nullptr; - CharacterClass *rest_cc = nullptr; - - auto *inner_seq = dynamic_cast(tb->ope_.get()); - if (inner_seq && inner_seq->opes_.size() == 2) { - first_cc = dynamic_cast(inner_seq->opes_[0].get()); - auto *rep = dynamic_cast(inner_seq->opes_[1].get()); - if (rep) { rest_cc = dynamic_cast(rep->ope_.get()); } - } - - if (!first_cc || !rest_cc) { return; } - if (!first_cc->is_ascii_only() || !rest_cc->is_ascii_only()) { return; } - - // All conditions met — set up the fast path - auto kw = std::make_unique(); - kw->identifier_first = first_cc->ascii_bitset(); - kw->identifier_rest = rest_cc->ascii_bitset(); - - // Compute keyword length range for early-out in hot path - size_t min_len = SIZE_MAX, max_len = 0; - for (const auto &k : exact_keywords) { - min_len = std::min(min_len, k.size()); - max_len = std::max(max_len, k.size()); - } - for (const auto &k : prefix_keywords) { - min_len = std::min(min_len, k.size()); - max_len = std::max(max_len, k.size()); - } - kw->min_keyword_len = min_len; - kw->max_keyword_len = max_len; - - kw->exact_keywords = std::move(exact_keywords); - kw->prefix_keywords = std::move(prefix_keywords); - seq.kw_guard_ = std::move(kw); -} - -// Compute which rules benefit from packrat memoization. -// A rule benefits if it's reachable from 2+ alternatives of the same -// PrioritizedChoice (backtracking will re-visit it at the same position). -inline void Definition::initialize_packrat_filter() const { - std::call_once(packrat_filter_init_, [&]() { - auto def_count = definition_ids_.size(); - if (def_count == 0) { return; } - - // Collect rule IDs reachable from an Ope subtree (bitvector indexed by - // def_id) - struct CollectReachableRules : public TraversalVisitor { - using TraversalVisitor::visit; - std::vector reachable; // indexed by def_id - - CollectReachableRules(size_t n) : reachable(n, false) {} - - void visit(Holder &ope) override { - auto id = ope.outer_->id; - if (id < reachable.size()) { reachable[id] = true; } - ope.ope_->accept(*this); - } - void visit(Reference &ope) override { - if (ope.rule_ && ope.rule_->id < reachable.size() && - !reachable[ope.rule_->id]) { - reachable[ope.rule_->id] = true; - ope.rule_->accept(*this); - } - } - }; - - // Find rules that benefit: reachable from 2+ alternatives of same choice - std::vector benefits(def_count, false); - - struct FindBacktrackRules : public TraversalVisitor { - using TraversalVisitor::visit; - std::vector &benefits; - size_t def_count; - std::vector visited_rules; // indexed by def_id - - FindBacktrackRules(std::vector &b, size_t n) - : benefits(b), def_count(n), visited_rules(n, false) {} - - void visit(PrioritizedChoice &ope) override { - // For each alternative, collect reachable rules as bitvectors - std::vector> alt_reachable; - for (auto &op : ope.opes_) { - CollectReachableRules crr(def_count); - op->accept(crr); - alt_reachable.push_back(std::move(crr.reachable)); - } - - // Mark rules reachable from 2+ alternatives - for (size_t id = 0; id < def_count; id++) { - size_t count = 0; - for (auto &alt : alt_reachable) { - if (alt[id]) { count++; } - } - if (count >= 2) { benefits[id] = true; } - } - - // Recurse into alternatives - for (auto &op : ope.opes_) { - op->accept(*this); - } - } - void visit(Holder &ope) override { - auto id = ope.outer_->id; - if (id < visited_rules.size() && !visited_rules[id]) { - visited_rules[id] = true; - ope.ope_->accept(*this); - } - } - void visit(Reference &ope) override { - if (ope.rule_) { ope.rule_->accept(*this); } - } - }; - - FindBacktrackRules finder(benefits, def_count); - holder_->accept(finder); - if (whitespaceOpe) { whitespaceOpe->accept(finder); } - if (wordOpe) { wordOpe->accept(finder); } - - packrat_filter_ = std::move(benefits); - }); -} - -inline void LinkReferences::visit(Reference &ope) { - // Check if the reference is a macro parameter - auto found_param = false; - for (size_t i = 0; i < params_.size(); i++) { - const auto ¶m = params_[i]; - if (param == ope.name_) { - ope.iarg_ = i; - found_param = true; - break; - } - } - - // Check if the reference is a definition rule - if (!found_param && grammar_.count(ope.name_)) { - auto &rule = grammar_.at(ope.name_); - ope.rule_ = &rule; - } - - for (const auto &arg : ope.args_) { - arg->accept(*this); - } -} - -inline void FindReference::visit(Reference &ope) { - for (size_t i = 0; i < args_.size(); i++) { - const auto &name = params_[i]; - if (name == ope.name_) { - found_ope = args_[i]; - return; - } - } - found_ope = ope.shared_from_this(); -} - -/*----------------------------------------------------------------------------- - * PEG parser generator - *---------------------------------------------------------------------------*/ - -using Rules = std::unordered_map>; - -class ParserGenerator { -public: - struct ParserContext { - std::shared_ptr grammar; - std::string start; - bool enablePackratParsing = false; - }; - - static ParserContext parse(const char *s, size_t n, const Rules &rules, - Log log, std::string_view start, - bool enable_left_recursion = true) { - return get_instance().perform_core(s, n, rules, log, std::string(start), - enable_left_recursion); - } - - // For debugging purpose - static bool parse_test(const char *d, const char *s) { - Data data; - std::any dt = &data; - - auto n = strlen(s); - auto r = get_instance().g[d].parse(s, n, dt); - return r.ret && r.len == n; - } - -#if defined(__cpp_lib_char8_t) - static bool parse_test(const char *d, const char8_t *s) { - return parse_test(d, reinterpret_cast(s)); - } -#endif - -private: - static ParserGenerator &get_instance() { - static ParserGenerator instance; - return instance; - } - - ParserGenerator() { - make_grammar(); - setup_actions(); - } - - struct Instruction { - std::string type; - std::any data; - std::string_view sv; - }; - - struct Data { - std::shared_ptr grammar; - std::string start; - const char *start_pos = nullptr; - - std::vector> duplicates_of_definition; - - std::vector> duplicates_of_instruction; - std::map> instructions; - - std::vector> undefined_back_references; - std::vector> captures_stack{{}}; - - std::set captures_in_current_definition; - bool enablePackratParsing = true; - - Data() : grammar(std::make_shared()) {} - }; - - class SyntaxErrorException : public std::runtime_error { - public: - SyntaxErrorException(const char *what_arg, std::pair r) - : std::runtime_error(what_arg), r_(r) {} - - std::pair line_info() const { return r_; } - - private: - std::pair r_; - }; - - void make_grammar() { - // Setup PEG syntax parser - g["Grammar"] <= seq(g["Spacing"], oom(g["Definition"]), g["EndOfFile"]); - g["Definition"] <= - cho(seq(g["Ignore"], g["IdentCont"], g["Parameters"], g["LEFTARROW"], - g["Expression"], opt(g["Instruction"])), - seq(g["Ignore"], g["Identifier"], g["LEFTARROW"], g["Expression"], - opt(g["Instruction"]))); - g["Expression"] <= seq(g["Sequence"], zom(seq(g["SLASH"], g["Sequence"]))); - g["Sequence"] <= zom(cho(g["CUT"], g["Prefix"])); - g["Prefix"] <= seq(opt(cho(g["AND"], g["NOT"])), g["SuffixWithLabel"]); - g["SuffixWithLabel"] <= - seq(g["Suffix"], opt(seq(g["LABEL"], g["Identifier"]))); - g["Suffix"] <= seq(g["Primary"], opt(g["Loop"])); - g["Loop"] <= cho(g["QUESTION"], g["STAR"], g["PLUS"], g["Repetition"]); - g["Primary"] <= cho(seq(g["Ignore"], g["IdentCont"], g["Arguments"], - npd(g["LEFTARROW"])), - seq(g["Ignore"], g["Identifier"], - npd(seq(opt(g["Parameters"]), g["LEFTARROW"]))), - seq(g["OPEN"], g["Expression"], g["CLOSE"]), - seq(g["BeginTok"], g["Expression"], g["EndTok"]), - g["CapScope"], - seq(g["BeginCap"], g["Expression"], g["EndCap"]), - g["BackRef"], g["DictionaryI"], g["LiteralI"], - g["Dictionary"], g["Literal"], g["NegatedClassI"], - g["NegatedClass"], g["ClassI"], g["Class"], g["DOT"]); - - g["Identifier"] <= seq(g["IdentCont"], g["Spacing"]); - g["IdentCont"] <= tok(seq(g["IdentStart"], zom(g["IdentRest"]))); - - const static std::vector> range = { - {0x0080, 0xFFFF}}; - g["IdentStart"] <= seq(npd(lit(u8(u8"↑"))), npd(lit(u8(u8"⇑"))), - cho(cls("a-zA-Z_%"), cls(range))); - - g["IdentRest"] <= cho(g["IdentStart"], cls("0-9")); - - g["Dictionary"] <= seq(g["LiteralD"], oom(seq(g["PIPE"], g["LiteralD"]))); - - g["DictionaryI"] <= - seq(g["LiteralID"], oom(seq(g["PIPE"], g["LiteralID"]))); - - auto lit_ope = cho(seq(cls("'"), tok(zom(seq(npd(cls("'")), g["Char"]))), - cls("'"), g["Spacing"]), - seq(cls("\""), tok(zom(seq(npd(cls("\"")), g["Char"]))), - cls("\""), g["Spacing"])); - g["Literal"] <= lit_ope; - g["LiteralD"] <= lit_ope; - - auto lit_case_ignore_ope = - cho(seq(cls("'"), tok(zom(seq(npd(cls("'")), g["Char"]))), lit("'i"), - g["Spacing"]), - seq(cls("\""), tok(zom(seq(npd(cls("\"")), g["Char"]))), lit("\"i"), - g["Spacing"])); - g["LiteralI"] <= lit_case_ignore_ope; - g["LiteralID"] <= lit_case_ignore_ope; - - // NOTE: The original Brian Ford's paper uses 'zom' instead of 'oom'. - g["Class"] <= seq(chr('['), npd(chr('^')), - tok(oom(seq(npd(chr(']')), g["Range"]))), chr(']'), - g["Spacing"]); - g["ClassI"] <= seq(chr('['), npd(chr('^')), - tok(oom(seq(npd(chr(']')), g["Range"]))), lit("]i"), - g["Spacing"]); - - g["NegatedClass"] <= seq(lit("[^"), - tok(oom(seq(npd(chr(']')), g["Range"]))), chr(']'), - g["Spacing"]); - g["NegatedClassI"] <= seq(lit("[^"), - tok(oom(seq(npd(chr(']')), g["Range"]))), - lit("]i"), g["Spacing"]); - - // NOTE: This is different from The original Brian Ford's paper, and this - // modification allows us to specify `[+-]` as a valid char class. - g["Range"] <= - cho(seq(g["Char"], chr('-'), npd(chr(']')), g["Char"]), g["Char"]); - - g["Char"] <= - cho(seq(chr('\\'), cls("fnrtv'\"[]\\^-")), - seq(chr('\\'), cls("0-3"), cls("0-7"), cls("0-7")), - seq(chr('\\'), cls("0-7"), opt(cls("0-7"))), - seq(lit("\\x"), cls("0-9a-fA-F"), opt(cls("0-9a-fA-F"))), - seq(lit("\\u"), - cho(seq(cho(seq(chr('0'), cls("0-9a-fA-F")), lit("10")), - rep(cls("0-9a-fA-F"), 4, 4)), - rep(cls("0-9a-fA-F"), 4, 5))), - seq(npd(chr('\\')), dot())); - - g["Repetition"] <= - seq(g["BeginBracket"], g["RepetitionRange"], g["EndBracket"]); - g["RepetitionRange"] <= cho(seq(g["Number"], g["COMMA"], g["Number"]), - seq(g["Number"], g["COMMA"]), g["Number"], - seq(g["COMMA"], g["Number"])); - g["Number"] <= seq(oom(cls("0-9")), g["Spacing"]); - - g["CapScope"] <= seq(g["BeginCapScope"], g["Expression"], g["EndCapScope"]); - - g["LEFTARROW"] <= seq(cho(lit("<-"), lit(u8(u8"←"))), g["Spacing"]); - ~g["SLASH"] <= seq(chr('/'), g["Spacing"]); - ~g["PIPE"] <= seq(chr('|'), g["Spacing"]); - g["AND"] <= seq(chr('&'), g["Spacing"]); - g["NOT"] <= seq(chr('!'), g["Spacing"]); - g["QUESTION"] <= seq(chr('?'), g["Spacing"]); - g["STAR"] <= seq(chr('*'), g["Spacing"]); - g["PLUS"] <= seq(chr('+'), g["Spacing"]); - ~g["OPEN"] <= seq(chr('('), g["Spacing"]); - ~g["CLOSE"] <= seq(chr(')'), g["Spacing"]); - g["DOT"] <= seq(chr('.'), g["Spacing"]); - - g["CUT"] <= seq(lit(u8(u8"↑")), g["Spacing"]); - ~g["LABEL"] <= seq(cho(chr('^'), lit(u8(u8"⇑"))), g["Spacing"]); - - ~g["Spacing"] <= zom(cho(g["Space"], g["Comment"])); - g["Comment"] <= seq(chr('#'), zom(seq(npd(g["EndOfLine"]), dot())), - opt(g["EndOfLine"])); - g["Space"] <= cho(chr(' '), chr('\t'), g["EndOfLine"]); - g["EndOfLine"] <= cho(lit("\r\n"), chr('\n'), chr('\r')); - g["EndOfFile"] <= npd(dot()); - - ~g["BeginTok"] <= seq(chr('<'), g["Spacing"]); - ~g["EndTok"] <= seq(chr('>'), g["Spacing"]); - - ~g["BeginCapScope"] <= seq(chr('$'), chr('('), g["Spacing"]); - ~g["EndCapScope"] <= seq(chr(')'), g["Spacing"]); - - g["BeginCap"] <= seq(chr('$'), tok(g["IdentCont"]), chr('<'), g["Spacing"]); - ~g["EndCap"] <= seq(chr('>'), g["Spacing"]); - - g["BackRef"] <= seq(chr('$'), tok(g["IdentCont"]), g["Spacing"]); - - g["IGNORE"] <= chr('~'); - - g["Ignore"] <= opt(g["IGNORE"]); - g["Parameters"] <= seq(g["OPEN"], g["Identifier"], - zom(seq(g["COMMA"], g["Identifier"])), g["CLOSE"]); - g["Arguments"] <= seq(g["OPEN"], g["Expression"], - zom(seq(g["COMMA"], g["Expression"])), g["CLOSE"]); - ~g["COMMA"] <= seq(chr(','), g["Spacing"]); - - // Instruction grammars - g["Instruction"] <= - seq(g["BeginBracket"], - opt(seq(g["InstructionItem"], zom(seq(g["InstructionItemSeparator"], - g["InstructionItem"])))), - g["EndBracket"]); - g["InstructionItem"] <= - cho(g["PrecedenceClimbing"], g["ErrorMessage"], g["NoAstOpt"]); - ~g["InstructionItemSeparator"] <= seq(chr(';'), g["Spacing"]); - - ~g["SpacesZom"] <= zom(g["Space"]); - ~g["SpacesOom"] <= oom(g["Space"]); - ~g["BeginBracket"] <= seq(chr('{'), g["Spacing"]); - ~g["EndBracket"] <= seq(chr('}'), g["Spacing"]); - - // PrecedenceClimbing instruction - g["PrecedenceClimbing"] <= - seq(lit("precedence"), g["SpacesOom"], g["PrecedenceInfo"], - zom(seq(g["SpacesOom"], g["PrecedenceInfo"])), g["SpacesZom"]); - g["PrecedenceInfo"] <= - seq(g["PrecedenceAssoc"], - oom(seq(ign(g["SpacesOom"]), g["PrecedenceOpe"]))); - g["PrecedenceOpe"] <= - cho(seq(cls("'"), - tok(zom(seq(npd(cho(g["Space"], cls("'"))), g["Char"]))), - cls("'")), - seq(cls("\""), - tok(zom(seq(npd(cho(g["Space"], cls("\""))), g["Char"]))), - cls("\"")), - tok(oom(seq(npd(cho(g["PrecedenceAssoc"], g["Space"], chr('}'))), - dot())))); - g["PrecedenceAssoc"] <= cls("LR"); - - // Error message instruction - g["ErrorMessage"] <= seq(lit("error_message"), g["SpacesOom"], - g["LiteralD"], g["SpacesZom"]); - - // No Ast node optimization instruction - g["NoAstOpt"] <= seq(lit("no_ast_opt"), g["SpacesZom"]); - - // Set definition names - for (auto &x : g) { - x.second.name = x.first; - } - } - - void setup_actions() { - g["Definition"] = [&](const SemanticValues &vs, std::any &dt) { - auto &data = *std::any_cast(dt); - - auto is_macro = vs.choice() == 0; - auto ignore = std::any_cast(vs[0]); - auto name = std::any_cast(vs[1]); - - std::vector params; - std::shared_ptr ope; - auto has_instructions = false; - - if (is_macro) { - params = std::any_cast>(vs[2]); - ope = std::any_cast>(vs[4]); - if (vs.size() == 6) { has_instructions = true; } - } else { - ope = std::any_cast>(vs[3]); - if (vs.size() == 5) { has_instructions = true; } - } - - if (has_instructions) { - auto index = is_macro ? 5 : 4; - std::unordered_set types; - for (const auto &instruction : - std::any_cast>(vs[index])) { - const auto &type = instruction.type; - if (types.find(type) == types.end()) { - data.instructions[name].push_back(instruction); - types.insert(instruction.type); - if (type == "declare_symbol" || type == "check_symbol") { - if (!TokenChecker::is_token(*ope)) { ope = tok(ope); } - } - } else { - data.duplicates_of_instruction.emplace_back(type, - instruction.sv.data()); - } - } - } - - auto &grammar = *data.grammar; - if (!grammar.count(name)) { - auto &rule = grammar[name]; - rule <= ope; - rule.name = name; - rule.s_ = vs.sv().data(); - rule.line_ = line_info(vs.ss, rule.s_); - rule.ignoreSemanticValue = ignore; - rule.is_macro = is_macro; - rule.params = params; - - if (data.start.empty()) { - data.start = rule.name; - data.start_pos = rule.s_; - } - } else { - data.duplicates_of_definition.emplace_back(name, vs.sv().data()); - } - }; - - g["Definition"].enter = [](const Context & /*c*/, const char * /*s*/, - size_t /*n*/, std::any &dt) { - auto &data = *std::any_cast(dt); - data.captures_in_current_definition.clear(); - }; - - g["Expression"] = [&](const SemanticValues &vs) { - if (vs.size() == 1) { - return std::any_cast>(vs[0]); - } else { - std::vector> opes; - for (auto i = 0u; i < vs.size(); i++) { - opes.emplace_back(std::any_cast>(vs[i])); - } - const std::shared_ptr ope = - std::make_shared(opes); - return ope; - } - }; - - g["Sequence"] = [&](const SemanticValues &vs) { - if (vs.empty()) { - return npd(lit("")); - } else if (vs.size() == 1) { - return std::any_cast>(vs[0]); - } else { - std::vector> opes; - for (const auto &x : vs) { - opes.emplace_back(std::any_cast>(x)); - } - const std::shared_ptr ope = std::make_shared(opes); - return ope; - } - }; - - g["Prefix"] = [&](const SemanticValues &vs) { - std::shared_ptr ope; - if (vs.size() == 1) { - ope = std::any_cast>(vs[0]); - } else { - assert(vs.size() == 2); - auto tok = std::any_cast(vs[0]); - ope = std::any_cast>(vs[1]); - if (tok == '&') { - ope = apd(ope); - } else { // '!' - ope = npd(ope); - } - } - return ope; - }; - - g["SuffixWithLabel"] = [&](const SemanticValues &vs, std::any &dt) { - auto ope = std::any_cast>(vs[0]); - if (vs.size() == 1) { - return ope; - } else { - assert(vs.size() == 2); - auto &data = *std::any_cast(dt); - const auto &ident = std::any_cast(vs[1]); - auto label = ref(*data.grammar, ident, vs.sv().data(), false, {}); - auto recovery = rec(ref(*data.grammar, RECOVER_DEFINITION_NAME, - vs.sv().data(), true, {label})); - return cho4label_(ope, recovery); - } - }; - - struct Loop { - enum class Type { opt = 0, zom, oom, rep }; - Type type; - std::pair range; - }; - - g["Suffix"] = [&](const SemanticValues &vs) { - auto ope = std::any_cast>(vs[0]); - if (vs.size() == 1) { - return ope; - } else { - assert(vs.size() == 2); - auto loop = std::any_cast(vs[1]); - switch (loop.type) { - case Loop::Type::opt: return opt(ope); - case Loop::Type::zom: return zom(ope); - case Loop::Type::oom: return oom(ope); - default: // Regex-like repetition - return rep(ope, loop.range.first, loop.range.second); - } - } - }; - - g["Loop"] = [&](const SemanticValues &vs) { - switch (vs.choice()) { - case 0: // Option - return Loop{Loop::Type::opt, std::pair()}; - case 1: // Zero or More - return Loop{Loop::Type::zom, std::pair()}; - case 2: // One or More - return Loop{Loop::Type::oom, std::pair()}; - default: // Regex-like repetition - return Loop{Loop::Type::rep, - std::any_cast>(vs[0])}; - } - }; - - g["Primary"] = [&](const SemanticValues &vs, std::any &dt) { - auto &data = *std::any_cast(dt); - - switch (vs.choice()) { - case 0: // Macro Reference - case 1: { // Reference - auto is_macro = vs.choice() == 0; - auto ignore = std::any_cast(vs[0]); - const auto &ident = std::any_cast(vs[1]); - - std::vector> args; - if (is_macro) { - args = std::any_cast>>(vs[2]); - } - - auto ope = ref(*data.grammar, ident, vs.sv().data(), is_macro, args); - if (ident == RECOVER_DEFINITION_NAME) { ope = rec(ope); } - - if (ignore) { - return ign(ope); - } else { - return ope; - } - } - case 2: { // (Expression) - return std::any_cast>(vs[0]); - } - case 3: { // TokenBoundary - return tok(std::any_cast>(vs[0])); - } - case 4: { // CaptureScope - return csc(std::any_cast>(vs[0])); - } - case 5: { // Capture - const auto &name = std::any_cast(vs[0]); - auto ope = std::any_cast>(vs[1]); - - data.captures_stack.back().insert(name); - data.captures_in_current_definition.insert(name); - - return cap(ope, [name](const char *a_s, size_t a_n, Context &c) { - c.capture_entries.emplace_back(name, std::string(a_s, a_n)); - }); - } - default: { - return std::any_cast>(vs[0]); - } - } - }; - - g["IdentCont"] = [](const SemanticValues &vs) { - return std::string(vs.sv().data(), vs.sv().length()); - }; - - g["Dictionary"] = [](const SemanticValues &vs) { - auto items = vs.transform(); - return dic(items, false); - }; - g["DictionaryI"] = [](const SemanticValues &vs) { - auto items = vs.transform(); - return dic(items, true); - }; - - g["Literal"] = [](const SemanticValues &vs) { - const auto &tok = vs.tokens.front(); - return lit(resolve_escape_sequence(tok.data(), tok.size())); - }; - g["LiteralI"] = [](const SemanticValues &vs) { - const auto &tok = vs.tokens.front(); - return liti(resolve_escape_sequence(tok.data(), tok.size())); - }; - g["LiteralD"] = [](const SemanticValues &vs) { - auto &tok = vs.tokens.front(); - return resolve_escape_sequence(tok.data(), tok.size()); - }; - g["LiteralID"] = [](const SemanticValues &vs) { - auto &tok = vs.tokens.front(); - return resolve_escape_sequence(tok.data(), tok.size()); - }; - - g["Class"] = [](const SemanticValues &vs) { - auto ranges = vs.transform>(); - return cls(ranges); - }; - g["ClassI"] = [](const SemanticValues &vs) { - auto ranges = vs.transform>(); - return cls(ranges, true); - }; - g["NegatedClass"] = [](const SemanticValues &vs) { - auto ranges = vs.transform>(); - return ncls(ranges); - }; - g["NegatedClassI"] = [](const SemanticValues &vs) { - auto ranges = vs.transform>(); - return ncls(ranges, true); - }; - g["Range"] = [](const SemanticValues &vs) { - switch (vs.choice()) { - case 0: { - auto s1 = std::any_cast(vs[0]); - auto s2 = std::any_cast(vs[1]); - auto cp1 = decode_codepoint(s1.data(), s1.length()); - auto cp2 = decode_codepoint(s2.data(), s2.length()); - if (cp1 > cp2) { - throw SyntaxErrorException("characer range is out of order...", - vs.line_info()); - } - return std::pair(cp1, cp2); - } - case 1: { - auto s = std::any_cast(vs[0]); - auto cp = decode_codepoint(s.data(), s.length()); - return std::pair(cp, cp); - } - } - return std::pair(0, 0); - }; - g["Char"] = [](const SemanticValues &vs) { - return resolve_escape_sequence(vs.sv().data(), vs.sv().length()); - }; - - g["RepetitionRange"] = [&](const SemanticValues &vs) { - switch (vs.choice()) { - case 0: { // Number COMMA Number - auto min = std::any_cast(vs[0]); - auto max = std::any_cast(vs[1]); - return std::pair(min, max); - } - case 1: // Number COMMA - return std::pair(std::any_cast(vs[0]), - std::numeric_limits::max()); - case 2: { // Number - auto n = std::any_cast(vs[0]); - return std::pair(n, n); - } - default: // COMMA Number - return std::pair(std::numeric_limits::min(), - std::any_cast(vs[0])); - } - }; - g["Number"] = [&](const SemanticValues &vs) { - return vs.token_to_number(); - }; - - g["CapScope"].enter = [](const Context & /*c*/, const char * /*s*/, - size_t /*n*/, std::any &dt) { - auto &data = *std::any_cast(dt); - data.captures_stack.emplace_back(); - }; - g["CapScope"].leave = [](const Context & /*c*/, const char * /*s*/, - size_t /*n*/, size_t /*matchlen*/, - std::any & /*value*/, std::any &dt) { - auto &data = *std::any_cast(dt); - data.captures_stack.pop_back(); - }; - - g["AND"] = [](const SemanticValues &vs) { return *vs.sv().data(); }; - g["NOT"] = [](const SemanticValues &vs) { return *vs.sv().data(); }; - g["QUESTION"] = [](const SemanticValues &vs) { return *vs.sv().data(); }; - g["STAR"] = [](const SemanticValues &vs) { return *vs.sv().data(); }; - g["PLUS"] = [](const SemanticValues &vs) { return *vs.sv().data(); }; - - g["DOT"] = [](const SemanticValues & /*vs*/) { return dot(); }; - - g["CUT"] = [](const SemanticValues & /*vs*/) { return cut(); }; - - g["BeginCap"] = [](const SemanticValues &vs) { return vs.token(); }; - - g["BackRef"] = [&](const SemanticValues &vs, std::any &dt) { - auto &data = *std::any_cast(dt); - - // Undefined back reference check - { - auto found = false; - auto it = data.captures_stack.rbegin(); - while (it != data.captures_stack.rend()) { - if (it->find(vs.token()) != it->end()) { - found = true; - break; - } - ++it; - } - if (!found) { - auto ptr = vs.token().data() - 1; // include '$' symbol - data.undefined_back_references.emplace_back(vs.token(), ptr); - } - } - - // NOTE: Disable packrat parsing if a back reference is not defined in - // captures in the current definition rule. - if (data.captures_in_current_definition.find(vs.token()) == - data.captures_in_current_definition.end()) { - data.enablePackratParsing = false; - } - - return bkr(vs.token_to_string()); - }; - - g["Ignore"] = [](const SemanticValues &vs) { return vs.size() > 0; }; - - g["Parameters"] = [](const SemanticValues &vs) { - return vs.transform(); - }; - - g["Arguments"] = [](const SemanticValues &vs) { - return vs.transform>(); - }; - - g["PrecedenceClimbing"] = [](const SemanticValues &vs) { - PrecedenceClimbing::BinOpeInfo binOpeInfo; - size_t level = 1; - for (const auto &v : vs) { - auto tokens = std::any_cast>(v); - auto assoc = tokens[0][0]; - for (size_t i = 1; i < tokens.size(); i++) { - binOpeInfo[tokens[i]] = std::pair(level, assoc); - } - level++; - } - Instruction instruction; - instruction.type = "precedence"; - instruction.data = binOpeInfo; - instruction.sv = vs.sv(); - return instruction; - }; - g["PrecedenceInfo"] = [](const SemanticValues &vs) { - return vs.transform(); - }; - g["PrecedenceOpe"] = [](const SemanticValues &vs) { return vs.token(); }; - g["PrecedenceAssoc"] = [](const SemanticValues &vs) { return vs.token(); }; - - g["ErrorMessage"] = [](const SemanticValues &vs) { - Instruction instruction; - instruction.type = "error_message"; - instruction.data = std::any_cast(vs[0]); - instruction.sv = vs.sv(); - return instruction; - }; - - g["NoAstOpt"] = [](const SemanticValues &vs) { - Instruction instruction; - instruction.type = "no_ast_opt"; - instruction.sv = vs.sv(); - return instruction; - }; - - g["Instruction"] = [](const SemanticValues &vs) { - return vs.transform(); - }; - } - - bool apply_precedence_instruction(Definition &rule, - const PrecedenceClimbing::BinOpeInfo &info, - const char *s, Log log) { - try { - auto &seq = dynamic_cast(*rule.get_core_operator()); - auto atom = seq.opes_[0]; - auto &rep = dynamic_cast(*seq.opes_[1]); - auto &seq1 = dynamic_cast(*rep.ope_); - auto binop = seq1.opes_[0]; - auto atom1 = seq1.opes_[1]; - - auto atom_name = dynamic_cast(*atom).name_; - auto binop_name = dynamic_cast(*binop).name_; - auto atom1_name = dynamic_cast(*atom1).name_; - - if (!rep.is_zom() || atom_name != atom1_name || atom_name == binop_name) { - if (log) { - auto line = line_info(s, rule.s_); - log(line.first, line.second, - "'precedence' instruction cannot be applied to '" + rule.name + - "'.", - ""); - } - return false; - } - - rule.holder_->ope_ = pre(atom, binop, info, rule); - rule.disable_action = true; - } catch (...) { - if (log) { - auto line = line_info(s, rule.s_); - log(line.first, line.second, - "'precedence' instruction cannot be applied to '" + rule.name + - "'.", - ""); - } - return false; - } - return true; - } - - ParserContext perform_core(const char *s, size_t n, const Rules &rules, - Log log, std::string requested_start, - bool enable_left_recursion = true) { - Data data; - auto &grammar = *data.grammar; - - // Built-in macros - { - // `%recover` - { - auto &rule = grammar[RECOVER_DEFINITION_NAME]; - rule <= ref(grammar, "x", "", false, {}); - rule.name = RECOVER_DEFINITION_NAME; - rule.s_ = "[native]"; - rule.ignoreSemanticValue = true; - rule.is_macro = true; - rule.params = {"x"}; - } - } - - try { - std::any dt = &data; - auto r = g["Grammar"].parse(s, n, dt, nullptr, log); - - if (!r.ret) { - if (log) { - if (r.error_info.message_pos) { - auto line = line_info(s, r.error_info.message_pos); - log(line.first, line.second, r.error_info.message, - r.error_info.label); - } else { - auto line = line_info(s, r.error_info.error_pos); - log(line.first, line.second, "syntax error", r.error_info.label); - } - } - return {}; - } - } catch (const SyntaxErrorException &e) { - if (log) { - auto line = e.line_info(); - log(line.first, line.second, e.what(), ""); - } - return {}; - } - - // User provided rules - for (auto [user_name, user_rule] : rules) { - auto name = user_name; - auto ignore = false; - if (!name.empty() && name[0] == '~') { - ignore = true; - name.erase(0, 1); - } - if (!name.empty()) { - auto &rule = grammar[name]; - rule <= user_rule; - rule.name = name; - rule.ignoreSemanticValue = ignore; - } - } - - // Check duplicated definitions - auto ret = true; - - if (!data.duplicates_of_definition.empty()) { - for (const auto &[name, ptr] : data.duplicates_of_definition) { - if (log) { - auto line = line_info(s, ptr); - log(line.first, line.second, - "the definition '" + name + "' is already defined.", ""); - } - } - ret = false; - } - - // Check duplicated instructions - if (!data.duplicates_of_instruction.empty()) { - for (const auto &[type, ptr] : data.duplicates_of_instruction) { - if (log) { - auto line = line_info(s, ptr); - log(line.first, line.second, - "the instruction '" + type + "' is already defined.", ""); - } - } - ret = false; - } - - // Check undefined back references - if (!data.undefined_back_references.empty()) { - for (const auto &[name, ptr] : data.undefined_back_references) { - if (log) { - auto line = line_info(s, ptr); - log(line.first, line.second, - "the back reference '" + name + "' is undefined.", ""); - } - } - ret = false; - } - - // Set root definition - auto start = data.start; - - if (!requested_start.empty()) { - if (grammar.count(requested_start)) { - start = requested_start; - } else { - if (log) { - auto line = line_info(s, s); - log(line.first, line.second, - "the specified start rule '" + requested_start + - "' is undefined.", - ""); - } - ret = false; - } - } - - if (!ret) { return {}; } - - auto &start_rule = grammar[start]; - - // Check if the start rule has ignore operator - { - if (start_rule.ignoreSemanticValue) { - if (log) { - auto line = line_info(s, start_rule.s_); - log(line.first, line.second, - "ignore operator cannot be applied to '" + start_rule.name + "'.", - ""); - } - ret = false; - } - } - - if (!ret) { return {}; } - - // Check missing definitions - auto referenced = std::unordered_set{ - WHITESPACE_DEFINITION_NAME, - WORD_DEFINITION_NAME, - RECOVER_DEFINITION_NAME, - start_rule.name, - }; - - for (auto &[_, rule] : grammar) { - ReferenceChecker vis(grammar, rule.params); - rule.accept(vis); - referenced.insert(vis.referenced.begin(), vis.referenced.end()); - for (const auto &[name, ptr] : vis.error_s) { - if (log) { - auto line = line_info(s, ptr); - log(line.first, line.second, vis.error_message[name], ""); - } - ret = false; - } - } - - for (auto &[name, rule] : grammar) { - if (!referenced.count(name)) { - if (log) { - auto line = line_info(s, rule.s_); - auto msg = "'" + name + "' is not referenced."; - log(line.first, line.second, msg, ""); - } - } - } - - if (!ret) { return {}; } - - // Link references - for (auto &x : grammar) { - auto &rule = x.second; - LinkReferences vis(grammar, rule.params); - rule.accept(vis); - } - - // Compute can_be_empty for each rule (fixed-point iteration) - { - bool changed = true; - while (changed) { - changed = false; - for (auto &[name, rule] : grammar) { - ComputeCanBeEmpty vis; - rule.accept(vis); - if (vis.result != rule.can_be_empty) { - rule.can_be_empty = vis.result; - changed = true; - } - } - } - } - - // Check left recursion - if (enable_left_recursion) { - for (auto &[name, rule] : grammar) { - DetectLeftRecursion vis(name); - rule.accept(vis); - if (vis.error_s) { rule.is_left_recursive = true; } - } - } else { - ret = true; - - for (auto &[name, rule] : grammar) { - DetectLeftRecursion vis(name); - rule.accept(vis); - if (vis.error_s) { - if (log) { - auto line = line_info(s, vis.error_s); - log(line.first, line.second, "'" + name + "' is left recursive.", - ""); - } - ret = false; - } - } - - if (!ret) { return {}; } - } - - // Check infinite loop - if (detect_infiniteLoop(data, start_rule, log, s)) { return {}; } - - // Automatic whitespace skipping - if (grammar.count(WHITESPACE_DEFINITION_NAME)) { - for (auto &x : grammar) { - auto &rule = x.second; - auto ope = rule.get_core_operator(); - if (IsLiteralToken::check(*ope)) { rule <= tok(ope); } - } - - auto &rule = grammar[WHITESPACE_DEFINITION_NAME]; - start_rule.whitespaceOpe = wsp(rule.get_core_operator()); - - if (detect_infiniteLoop(data, rule, log, s)) { return {}; } - } - - // Word expression - if (grammar.count(WORD_DEFINITION_NAME)) { - auto &rule = grammar[WORD_DEFINITION_NAME]; - start_rule.wordOpe = rule.get_core_operator(); - - if (detect_infiniteLoop(data, rule, log, s)) { return {}; } - } - - // Apply instructions - for (const auto &[name, instructions] : data.instructions) { - auto &rule = grammar[name]; - - for (const auto &instruction : instructions) { - if (instruction.type == "precedence") { - const auto &info = - std::any_cast(instruction.data); - - if (!apply_precedence_instruction(rule, info, s, log)) { return {}; } - } else if (instruction.type == "error_message") { - rule.error_message = std::any_cast(instruction.data); - } else if (instruction.type == "no_ast_opt") { - rule.no_ast_opt = true; - } - } - } - - // Setup First-Set and ISpan optimizations - for (auto &x : grammar) { - SetupFirstSets vis; - x.second.accept(vis); - } - - return {data.grammar, start, data.enablePackratParsing}; - } - - bool detect_infiniteLoop(const Data &data, Definition &rule, const Log &log, - const char *s) const { - std::vector> refs; - std::unordered_map has_error_cache; - DetectInfiniteLoop vis(data.start_pos, rule.name, refs, has_error_cache); - rule.accept(vis); - if (vis.has_error) { - if (log) { - auto line = line_info(s, vis.error_s); - log(line.first, line.second, - "infinite loop is detected in '" + vis.error_name + "'.", ""); - } - return true; - } - return false; - } - - Grammar g; -}; - -/*----------------------------------------------------------------------------- - * AST - *---------------------------------------------------------------------------*/ - -template struct AstBase : public Annotation { - AstBase(const char *path, size_t line, size_t column, const char *name, - const std::vector> &nodes, - size_t position = 0, size_t length = 0, size_t choice_count = 0, - size_t choice = 0) - : path(path ? path : ""), line(line), column(column), name(name), - position(position), length(length), choice_count(choice_count), - choice(choice), original_name(name), - original_choice_count(choice_count), original_choice(choice), - tag(str2tag(name)), original_tag(tag), is_token(false), nodes(nodes) {} - - AstBase(const char *path, size_t line, size_t column, const char *name, - const std::string_view &token, size_t position = 0, size_t length = 0, - size_t choice_count = 0, size_t choice = 0) - : path(path ? path : ""), line(line), column(column), name(name), - position(position), length(length), choice_count(choice_count), - choice(choice), original_name(name), - original_choice_count(choice_count), original_choice(choice), - tag(str2tag(name)), original_tag(tag), is_token(true), token(token) {} - - AstBase(const AstBase &ast, const char *original_name, size_t position = 0, - size_t length = 0, size_t original_choice_count = 0, - size_t original_choice = 0) - : path(ast.path), line(ast.line), column(ast.column), name(ast.name), - position(position), length(length), choice_count(ast.choice_count), - choice(ast.choice), original_name(original_name), - original_choice_count(original_choice_count), - original_choice(original_choice), tag(ast.tag), - original_tag(str2tag(original_name)), is_token(ast.is_token), - token(ast.token), nodes(ast.nodes), parent(ast.parent) {} - - const std::string path; - const size_t line = 1; - const size_t column = 1; - - const std::string name; - size_t position; - size_t length; - const size_t choice_count; - const size_t choice; - const std::string original_name; - const size_t original_choice_count; - const size_t original_choice; - const unsigned int tag; - const unsigned int original_tag; - - const bool is_token; - const std::string_view token; - - std::vector>> nodes; - std::weak_ptr> parent; - - std::string token_to_string() const { - assert(is_token); - return std::string(token); - } - - template T token_to_number() const { - return token_to_number_(token); - } -}; - -template -void ast_to_s_core(const std::shared_ptr &ptr, std::string &s, int level, - std::function fn) { - const auto &ast = *ptr; - for (auto i = 0; i < level; i++) { - s += " "; - } - auto name = ast.original_name; - if (ast.original_choice_count > 0) { - name += "/" + std::to_string(ast.original_choice); - } - if (ast.name != ast.original_name) { name += "[" + ast.name + "]"; } - if (ast.is_token) { - s += "- " + name + " ("; - s += ast.token; - s += ")\n"; - } else { - s += "+ " + name + "\n"; - } - if (fn) { s += fn(ast, level + 1); } - for (const auto &node : ast.nodes) { - ast_to_s_core(node, s, level + 1, fn); - } -} - -template -std::string -ast_to_s(const std::shared_ptr &ptr, - std::function fn = nullptr) { - std::string s; - ast_to_s_core(ptr, s, 0, fn); - return s; -} - -struct AstOptimizer { - AstOptimizer(bool mode, const std::vector &rules = {}) - : mode_(mode), rules_(rules) {} - - template - std::shared_ptr optimize(std::shared_ptr original, - std::shared_ptr parent = nullptr) { - auto found = - std::find(rules_.begin(), rules_.end(), original->name) != rules_.end(); - auto opt = mode_ ? !found : found; - - if (opt && original->nodes.size() == 1) { - auto child = optimize(original->nodes[0], parent); - auto ast = std::make_shared(*child, original->name.data(), - original->position, original->length, - original->choice_count, original->choice); - for (auto &node : ast->nodes) { - node->parent = ast; - } - return ast; - } - - auto ast = std::make_shared(*original); - ast->parent = parent; - ast->nodes.clear(); - for (const auto &node : original->nodes) { - auto child = optimize(node, ast); - ast->nodes.push_back(child); - } - return ast; - } - -private: - const bool mode_; - const std::vector rules_; -}; - -struct EmptyType {}; -using Ast = AstBase; - -template void add_ast_action(Definition &rule) { - rule.action = [&](const SemanticValues &vs) { - auto line = vs.line_info(); - - if (rule.is_token()) { - return std::make_shared( - vs.path, line.first, line.second, rule.name.data(), vs.token(), - std::distance(vs.ss, vs.sv().data()), vs.sv().length(), - vs.choice_count(), vs.choice()); - } - - auto ast = - std::make_shared(vs.path, line.first, line.second, rule.name.data(), - vs.transform>(), - std::distance(vs.ss, vs.sv().data()), - vs.sv().length(), vs.choice_count(), vs.choice()); - - for (auto &node : ast->nodes) { - node->parent = ast; - } - return ast; - }; -} - -#define PEG_EXPAND(...) __VA_ARGS__ -#define PEG_CONCAT(a, b) a##b -#define PEG_CONCAT2(a, b) PEG_CONCAT(a, b) - -#define PEG_PICK( \ - a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, \ - a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, \ - a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, \ - a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, \ - a62, a63, a64, a65, a66, a67, a68, a69, a70, a71, a72, a73, a74, a75, a76, \ - a77, a78, a79, a80, a81, a82, a83, a84, a85, a86, a87, a88, a89, a90, a91, \ - a92, a93, a94, a95, a96, a97, a98, a99, a100, ...) \ - a100 - -#define PEG_COUNT(...) \ - PEG_EXPAND(PEG_PICK( \ - __VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, \ - 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, \ - 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ - 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, \ - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, \ - 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) - -#define PEG_DEF_1(r) \ - peg::Definition r; \ - r.name = #r; \ - peg::add_ast_action(r); - -#define PEG_DEF_2(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_1(__VA_ARGS__)) -#define PEG_DEF_3(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_2(__VA_ARGS__)) -#define PEG_DEF_4(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_3(__VA_ARGS__)) -#define PEG_DEF_5(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_4(__VA_ARGS__)) -#define PEG_DEF_6(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_5(__VA_ARGS__)) -#define PEG_DEF_7(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_6(__VA_ARGS__)) -#define PEG_DEF_8(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_7(__VA_ARGS__)) -#define PEG_DEF_9(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_8(__VA_ARGS__)) -#define PEG_DEF_10(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_9(__VA_ARGS__)) -#define PEG_DEF_11(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_10(__VA_ARGS__)) -#define PEG_DEF_12(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_11(__VA_ARGS__)) -#define PEG_DEF_13(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_12(__VA_ARGS__)) -#define PEG_DEF_14(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_13(__VA_ARGS__)) -#define PEG_DEF_15(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_14(__VA_ARGS__)) -#define PEG_DEF_16(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_15(__VA_ARGS__)) -#define PEG_DEF_17(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_16(__VA_ARGS__)) -#define PEG_DEF_18(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_17(__VA_ARGS__)) -#define PEG_DEF_19(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_18(__VA_ARGS__)) -#define PEG_DEF_20(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_19(__VA_ARGS__)) -#define PEG_DEF_21(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_20(__VA_ARGS__)) -#define PEG_DEF_22(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_21(__VA_ARGS__)) -#define PEG_DEF_23(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_22(__VA_ARGS__)) -#define PEG_DEF_24(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_23(__VA_ARGS__)) -#define PEG_DEF_25(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_24(__VA_ARGS__)) -#define PEG_DEF_26(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_25(__VA_ARGS__)) -#define PEG_DEF_27(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_26(__VA_ARGS__)) -#define PEG_DEF_28(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_27(__VA_ARGS__)) -#define PEG_DEF_29(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_28(__VA_ARGS__)) -#define PEG_DEF_30(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_29(__VA_ARGS__)) -#define PEG_DEF_31(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_30(__VA_ARGS__)) -#define PEG_DEF_32(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_31(__VA_ARGS__)) -#define PEG_DEF_33(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_32(__VA_ARGS__)) -#define PEG_DEF_34(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_33(__VA_ARGS__)) -#define PEG_DEF_35(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_34(__VA_ARGS__)) -#define PEG_DEF_36(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_35(__VA_ARGS__)) -#define PEG_DEF_37(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_36(__VA_ARGS__)) -#define PEG_DEF_38(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_37(__VA_ARGS__)) -#define PEG_DEF_39(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_38(__VA_ARGS__)) -#define PEG_DEF_40(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_39(__VA_ARGS__)) -#define PEG_DEF_41(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_40(__VA_ARGS__)) -#define PEG_DEF_42(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_41(__VA_ARGS__)) -#define PEG_DEF_43(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_42(__VA_ARGS__)) -#define PEG_DEF_44(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_43(__VA_ARGS__)) -#define PEG_DEF_45(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_44(__VA_ARGS__)) -#define PEG_DEF_46(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_45(__VA_ARGS__)) -#define PEG_DEF_47(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_46(__VA_ARGS__)) -#define PEG_DEF_48(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_47(__VA_ARGS__)) -#define PEG_DEF_49(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_48(__VA_ARGS__)) -#define PEG_DEF_50(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_49(__VA_ARGS__)) -#define PEG_DEF_51(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_50(__VA_ARGS__)) -#define PEG_DEF_52(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_51(__VA_ARGS__)) -#define PEG_DEF_53(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_52(__VA_ARGS__)) -#define PEG_DEF_54(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_53(__VA_ARGS__)) -#define PEG_DEF_55(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_54(__VA_ARGS__)) -#define PEG_DEF_56(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_55(__VA_ARGS__)) -#define PEG_DEF_57(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_56(__VA_ARGS__)) -#define PEG_DEF_58(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_57(__VA_ARGS__)) -#define PEG_DEF_59(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_58(__VA_ARGS__)) -#define PEG_DEF_60(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_59(__VA_ARGS__)) -#define PEG_DEF_61(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_60(__VA_ARGS__)) -#define PEG_DEF_62(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_61(__VA_ARGS__)) -#define PEG_DEF_63(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_62(__VA_ARGS__)) -#define PEG_DEF_64(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_63(__VA_ARGS__)) -#define PEG_DEF_65(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_64(__VA_ARGS__)) -#define PEG_DEF_66(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_65(__VA_ARGS__)) -#define PEG_DEF_67(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_66(__VA_ARGS__)) -#define PEG_DEF_68(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_67(__VA_ARGS__)) -#define PEG_DEF_69(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_68(__VA_ARGS__)) -#define PEG_DEF_70(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_69(__VA_ARGS__)) -#define PEG_DEF_71(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_70(__VA_ARGS__)) -#define PEG_DEF_72(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_71(__VA_ARGS__)) -#define PEG_DEF_73(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_72(__VA_ARGS__)) -#define PEG_DEF_74(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_73(__VA_ARGS__)) -#define PEG_DEF_75(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_74(__VA_ARGS__)) -#define PEG_DEF_76(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_75(__VA_ARGS__)) -#define PEG_DEF_77(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_76(__VA_ARGS__)) -#define PEG_DEF_78(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_77(__VA_ARGS__)) -#define PEG_DEF_79(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_78(__VA_ARGS__)) -#define PEG_DEF_80(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_79(__VA_ARGS__)) -#define PEG_DEF_81(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_80(__VA_ARGS__)) -#define PEG_DEF_82(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_81(__VA_ARGS__)) -#define PEG_DEF_83(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_82(__VA_ARGS__)) -#define PEG_DEF_84(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_83(__VA_ARGS__)) -#define PEG_DEF_85(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_84(__VA_ARGS__)) -#define PEG_DEF_86(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_85(__VA_ARGS__)) -#define PEG_DEF_87(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_86(__VA_ARGS__)) -#define PEG_DEF_88(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_87(__VA_ARGS__)) -#define PEG_DEF_89(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_88(__VA_ARGS__)) -#define PEG_DEF_90(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_89(__VA_ARGS__)) -#define PEG_DEF_91(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_90(__VA_ARGS__)) -#define PEG_DEF_92(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_91(__VA_ARGS__)) -#define PEG_DEF_93(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_92(__VA_ARGS__)) -#define PEG_DEF_94(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_93(__VA_ARGS__)) -#define PEG_DEF_95(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_94(__VA_ARGS__)) -#define PEG_DEF_96(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_95(__VA_ARGS__)) -#define PEG_DEF_97(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_96(__VA_ARGS__)) -#define PEG_DEF_98(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_97(__VA_ARGS__)) -#define PEG_DEF_99(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_98(__VA_ARGS__)) -#define PEG_DEF_100(r1, ...) PEG_EXPAND(PEG_DEF_1(r1) PEG_DEF_99(__VA_ARGS__)) - -#define AST_DEFINITIONS(...) \ - PEG_EXPAND(PEG_CONCAT2(PEG_DEF_, PEG_COUNT(__VA_ARGS__))(__VA_ARGS__)) - -/*----------------------------------------------------------------------------- - * parser - *---------------------------------------------------------------------------*/ - -class parser { -public: - parser() = default; - - parser(const char *s, size_t n, const Rules &rules, - std::string_view start = {}) { - load_grammar(s, n, rules, start); - } - - parser(const char *s, size_t n, std::string_view start = {}) - : parser(s, n, Rules(), start) {} - - parser(std::string_view sv, const Rules &rules, std::string_view start = {}) - : parser(sv.data(), sv.size(), rules, start) {} - - parser(std::string_view sv, std::string_view start = {}) - : parser(sv.data(), sv.size(), Rules(), start) {} - -#if defined(__cpp_lib_char8_t) - parser(std::u8string_view sv, const Rules &rules, std::string_view start = {}) - : parser(reinterpret_cast(sv.data()), sv.size(), rules, - start) {} - - parser(std::u8string_view sv, std::string_view start = {}) - : parser(reinterpret_cast(sv.data()), sv.size(), Rules(), - start) {} -#endif - - operator bool() const { return grammar_ != nullptr; } - - bool load_grammar(const char *s, size_t n, const Rules &rules, - std::string_view start = {}) { - auto cxt = - ParserGenerator::parse(s, n, rules, log_, start, enableLeftRecursion_); - grammar_ = cxt.grammar; - start_ = cxt.start; - enablePackratParsing_ = cxt.enablePackratParsing; - return grammar_ != nullptr; - } - - bool load_grammar(const char *s, size_t n, std::string_view start = {}) { - return load_grammar(s, n, Rules(), start); - } - - bool load_grammar(std::string_view sv, const Rules &rules, - std::string_view start = {}) { - return load_grammar(sv.data(), sv.size(), rules, start); - } - - bool load_grammar(std::string_view sv, std::string_view start = {}) { - return load_grammar(sv.data(), sv.size(), Rules(), start); - } - - bool parse_n(const char *s, size_t n, const char *path = nullptr) const { - if (grammar_ != nullptr) { - const auto &rule = (*grammar_)[start_]; - auto result = rule.parse(s, n, path, log_); - return post_process(s, n, result); - } - return false; - } - - bool parse_n(const char *s, size_t n, std::any &dt, - const char *path = nullptr) const { - if (grammar_ != nullptr) { - const auto &rule = (*grammar_)[start_]; - auto result = rule.parse(s, n, dt, path, log_); - return post_process(s, n, result); - } - return false; - } - - template - bool parse_n(const char *s, size_t n, T &val, - const char *path = nullptr) const { - if (grammar_ != nullptr) { - const auto &rule = (*grammar_)[start_]; - auto result = rule.parse_and_get_value(s, n, val, path, log_); - return post_process(s, n, result); - } - return false; - } - - template - bool parse_n(const char *s, size_t n, std::any &dt, T &val, - const char *path = nullptr) const { - if (grammar_ != nullptr) { - const auto &rule = (*grammar_)[start_]; - auto result = rule.parse_and_get_value(s, n, dt, val, path, log_); - return post_process(s, n, result); - } - return false; - } - - bool parse(std::string_view sv, const char *path = nullptr) const { - return parse_n(sv.data(), sv.size(), path); - } - - bool parse(std::string_view sv, std::any &dt, - const char *path = nullptr) const { - return parse_n(sv.data(), sv.size(), dt, path); - } - - template - bool parse(std::string_view sv, T &val, const char *path = nullptr) const { - return parse_n(sv.data(), sv.size(), val, path); - } - - template - bool parse(std::string_view sv, std::any &dt, T &val, - const char *path = nullptr) const { - return parse_n(sv.data(), sv.size(), dt, val, path); - } - -#if defined(__cpp_lib_char8_t) - bool parse(std::u8string_view sv, const char *path = nullptr) const { - return parse_n(reinterpret_cast(sv.data()), sv.size(), path); - } - - bool parse(std::u8string_view sv, std::any &dt, - const char *path = nullptr) const { - return parse_n(reinterpret_cast(sv.data()), sv.size(), dt, - path); - } - - template - bool parse(std::u8string_view sv, T &val, const char *path = nullptr) const { - return parse_n(reinterpret_cast(sv.data()), sv.size(), val, - path); - } - - template - bool parse(std::u8string_view sv, std::any &dt, T &val, - const char *path = nullptr) const { - return parse_n(reinterpret_cast(sv.data()), sv.size(), dt, - val, path); - } -#endif - - Definition &operator[](const char *s) { return (*grammar_)[s]; } - - const Definition &operator[](const char *s) const { return (*grammar_)[s]; } - - const Grammar &get_grammar() const { return *grammar_; } - - void disable_eoi_check() { - if (grammar_ != nullptr) { - auto &rule = (*grammar_)[start_]; - rule.eoi_check = false; - } - } - - void enable_left_recursion(bool enable = true) { - enableLeftRecursion_ = enable; - } - - void enable_packrat_parsing() { - if (grammar_ != nullptr) { - auto &rule = (*grammar_)[start_]; - rule.enablePackratParsing = enablePackratParsing_; - } - } - - void enable_trace(TracerEnter tracer_enter, TracerLeave tracer_leave) { - if (grammar_ != nullptr) { - auto &rule = (*grammar_)[start_]; - rule.tracer_enter = tracer_enter; - rule.tracer_leave = tracer_leave; - } - } - - void enable_trace(TracerEnter tracer_enter, TracerLeave tracer_leave, - TracerStartOrEnd tracer_start, - TracerStartOrEnd tracer_end) { - if (grammar_ != nullptr) { - auto &rule = (*grammar_)[start_]; - rule.tracer_enter = tracer_enter; - rule.tracer_leave = tracer_leave; - rule.tracer_start = tracer_start; - rule.tracer_end = tracer_end; - } - } - - void set_verbose_trace(bool verbose_trace) { - if (grammar_ != nullptr) { - auto &rule = (*grammar_)[start_]; - rule.verbose_trace = verbose_trace; - } - } - - template parser &enable_ast() { - for (auto &[_, rule] : *grammar_) { - if (!rule.action) { add_ast_action(rule); } - } - return *this; - } - - template - std::shared_ptr optimize_ast(std::shared_ptr ast, - bool opt_mode = true) const { - return AstOptimizer(opt_mode, get_no_ast_opt_rules()).optimize(ast); - } - - void set_logger(Log log) { log_ = log; } - - void set_logger( - std::function - log) { - log_ = [log](size_t line, size_t col, const std::string &msg, - const std::string & /*rule*/) { log(line, col, msg); }; - } - -private: - bool post_process(const char *s, size_t n, Definition::Result &r) const { - if (log_ && !r.ret) { r.error_info.output_log(log_, s, n); } - return r.ret && !r.recovered; - } - - std::vector get_no_ast_opt_rules() const { - std::vector rules; - for (auto &[name, rule] : *grammar_) { - if (rule.no_ast_opt) { rules.push_back(name); } - } - return rules; - } - - std::shared_ptr grammar_; - std::string start_; - bool enableLeftRecursion_ = true; - bool enablePackratParsing_ = false; - Log log_; -}; - -/*----------------------------------------------------------------------------- - * enable_tracing - *---------------------------------------------------------------------------*/ - -inline void enable_tracing(parser &parser, std::ostream &os) { - parser.enable_trace( - [&](auto &ope, auto s, auto, auto &, auto &c, auto &, auto &trace_data) { - auto prev_pos = std::any_cast(trace_data); - auto pos = static_cast(s - c.s); - auto backtrack = (pos < prev_pos ? "*" : ""); - std::string indent; - auto level = c.trace_ids.size() - 1; - while (level--) { - indent += "│"; - } - std::string name; - { - name = peg::TraceOpeName::get(const_cast(ope)); - - auto lit = dynamic_cast(&ope); - if (lit) { name += " '" + peg::escape_characters(lit->lit_) + "'"; } - } - os << "E " << pos + 1 << backtrack << "\t" << indent << "┌" << name - << " #" << c.trace_ids.back() << std::endl; - trace_data = static_cast(pos); - }, - [&](auto &ope, auto s, auto, auto &sv, auto &c, auto &, auto len, - auto &) { - auto pos = static_cast(s - c.s); - if (len != static_cast(-1)) { pos += len; } - std::string indent; - auto level = c.trace_ids.size() - 1; - while (level--) { - indent += "│"; - } - auto ret = len != static_cast(-1) ? "└o " : "└x "; - auto name = peg::TraceOpeName::get(const_cast(ope)); - std::stringstream choice; - if (sv.choice_count() > 0) { - choice << " " << sv.choice() << "/" << sv.choice_count(); - } - std::string token; - if (!sv.tokens.empty()) { - token += ", token '"; - token += sv.tokens[0]; - token += "'"; - } - std::string matched; - if (peg::success(len) && - peg::TokenChecker::is_token(const_cast(ope))) { - matched = ", match '" + peg::escape_characters(s, len) + "'"; - } - os << "L " << pos + 1 << "\t" << indent << ret << name << " #" - << c.trace_ids.back() << choice.str() << token << matched - << std::endl; - }, - [&](auto &trace_data) { trace_data = static_cast(0); }, - [&](auto &) {}); -} - -/*----------------------------------------------------------------------------- - * enable_profiling - *---------------------------------------------------------------------------*/ - -inline void enable_profiling(parser &parser, std::ostream &os) { - struct Stats { - struct Item { - std::string name; - size_t success; - size_t fail; - }; - std::vector items; - std::map index; - size_t total = 0; - std::chrono::steady_clock::time_point start; - }; - - parser.enable_trace( - [&](auto &ope, auto, auto, auto &, auto &, auto &, std::any &trace_data) { - if (auto holder = dynamic_cast(&ope)) { - auto &stats = *std::any_cast(trace_data); - - auto &name = holder->name(); - if (stats.index.find(name) == stats.index.end()) { - stats.index[name] = stats.index.size(); - stats.items.push_back({name, 0, 0}); - } - stats.total++; - } - }, - [&](auto &ope, auto, auto, auto &, auto &, auto &, auto len, - std::any &trace_data) { - if (auto holder = dynamic_cast(&ope)) { - auto &stats = *std::any_cast(trace_data); - - auto &name = holder->name(); - auto index = stats.index[name]; - auto &stat = stats.items[index]; - if (len != static_cast(-1)) { - stat.success++; - } else { - stat.fail++; - } - - if (index == 0) { - auto end = std::chrono::steady_clock::now(); - auto nano = std::chrono::duration_cast( - end - stats.start) - .count(); - auto sec = nano / 1000000.0; - os << "duration: " << sec << "s (" << nano << "µs)" << std::endl - << std::endl; - - char buff[BUFSIZ]; - size_t total_success = 0; - size_t total_fail = 0; - for (auto &[name, success, fail] : stats.items) { - total_success += success; - total_fail += fail; - } - - os << " id total % success fail " - "definition" - << std::endl; - - auto grand_total = total_success + total_fail; - snprintf(buff, BUFSIZ, "%4s %10zu %5s %10zu %10zu %s", "", - grand_total, "", total_success, total_fail, - "Total counters"); - os << buff << std::endl; - - snprintf(buff, BUFSIZ, "%4s %10s %5s %10.2f %10.2f %s", "", "", - "", total_success * 100.0 / grand_total, - total_fail * 100.0 / grand_total, "% success/fail"); - os << buff << std::endl << std::endl; - ; - - size_t id = 0; - for (auto &[name, success, fail] : stats.items) { - auto total = success + fail; - auto ratio = total * 100.0 / stats.total; - snprintf(buff, BUFSIZ, "%4zu %10zu %5.2f %10zu %10zu %s", id, - total, ratio, success, fail, name.c_str()); - os << buff << std::endl; - id++; - } - } - } - }, - [&](auto &trace_data) { - auto stats = new Stats{}; - stats->start = std::chrono::steady_clock::now(); - trace_data = stats; - }, - [&](auto &trace_data) { - auto stats = std::any_cast(trace_data); - delete stats; - }); -} -} // namespace peg diff --git a/libcockatrice_utility/libcockatrice/utility/qt_utils.h b/libcockatrice_utility/libcockatrice/utility/qt_utils.h deleted file mode 100644 index 606947143..000000000 --- a/libcockatrice_utility/libcockatrice/utility/qt_utils.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef COCKATRICE_QT_UTILS_H -#define COCKATRICE_QT_UTILS_H -#include - -namespace QtUtils -{ -template T *findParentOfType(const QObject *obj) -{ - const QObject *p = obj ? obj->parent() : nullptr; - while (p) { - if (auto casted = qobject_cast(const_cast(p))) { - return casted; - } - p = p->parent(); - } - return nullptr; -} - -static inline void clearLayoutRec(QLayout *l) -{ - if (!l) - return; - QLayoutItem *it; - while ((it = l->takeAt(0)) != nullptr) { - if (QWidget *w = it->widget()) - w->deleteLater(); - if (QLayout *sub = it->layout()) - clearLayoutRec(sub); - delete it; - } -} -} // namespace QtUtils - -#endif // COCKATRICE_QT_UTILS_H diff --git a/libcockatrice_utility/libcockatrice/utility/trice_limits.h b/libcockatrice_utility/libcockatrice/utility/trice_limits.h deleted file mode 100644 index fa7ce7489..000000000 --- a/libcockatrice_utility/libcockatrice/utility/trice_limits.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TRICE_LIMITS_H -#define TRICE_LIMITS_H - -#include - -// max size for short strings, like names and things that are generally a single phrase -constexpr int MAX_NAME_LENGTH = 0xff; -// max size for chat messages and text contents -constexpr int MAX_TEXT_LENGTH = 0xfff; -// max size for deck files and pictures -constexpr int MAX_FILE_LENGTH = 0x1fffff; // about 2 megabytes - -constexpr uint MINIMUM_DIE_SIDES = 2; -constexpr uint MAXIMUM_DIE_SIDES = 1000000; -constexpr uint MINIMUM_DICE_TO_ROLL = 1; -constexpr uint MAXIMUM_DICE_TO_ROLL = 100; - -// optimized functions to get qstrings that are at most that long -static inline QString nameFromStdString(const std::string &_string) -{ - return QString::fromUtf8(_string.data(), std::min(int(_string.size()), MAX_NAME_LENGTH)); -} -static inline QString textFromStdString(const std::string &_string) -{ - return QString::fromUtf8(_string.data(), std::min(int(_string.size()), MAX_TEXT_LENGTH)); -} -static inline QString fileFromStdString(const std::string &_string) -{ - return QString::fromUtf8(_string.data(), std::min(int(_string.size()), MAX_FILE_LENGTH)); -} - -#endif // TRICE_LIMITS_H diff --git a/libcockatrice_utility/libcockatrice/utility/zone_names.h b/libcockatrice_utility/libcockatrice/utility/zone_names.h deleted file mode 100644 index d1463de6a..000000000 --- a/libcockatrice_utility/libcockatrice/utility/zone_names.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ZONE_NAMES_H -#define ZONE_NAMES_H - -namespace ZoneNames -{ -// Protocol-level zone identifiers shared between client and server. -// These must match exactly across all components. - -constexpr const char *TABLE = "table"; -constexpr const char *GRAVE = "grave"; -constexpr const char *EXILE = "rfg"; // "removed from game" -constexpr const char *HAND = "hand"; -constexpr const char *DECK = "deck"; -constexpr const char *SIDEBOARD = "sb"; -constexpr const char *STACK = "stack"; - -} // namespace ZoneNames - -#endif // ZONE_NAMES_H diff --git a/oracle/CMakeLists.txt b/oracle/CMakeLists.txt index 3bb4de5df..7851c1d2b 100644 --- a/oracle/CMakeLists.txt +++ b/oracle/CMakeLists.txt @@ -1,316 +1,247 @@ -cmake_minimum_required(VERSION 3.16) -project(Oracle VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +# CMakeLists for oracle directory +# +# provides the oracle binary -# ------------------------ -# Paths and directories -# ------------------------ -set(DESKTOPDIR - share/applications - CACHE STRING "path to .desktop files" -) +PROJECT(oracle) -set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations") -set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations") -set(ORACLE_WIN32_QM_INSTALL_DIR "translations") +# paths +set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files") -# ------------------------ -# Sources -# ------------------------ -set(oracle_SOURCES +SET(oracle_SOURCES src/main.cpp src/oraclewizard.cpp src/oracleimporter.cpp - src/pages.cpp - src/pagetemplates.cpp - src/parsehelpers.cpp - src/qt-json/json.cpp - ../cockatrice/src/client/settings/cache_settings.cpp - ../cockatrice/src/client/settings/card_counter_settings.cpp - ../cockatrice/src/client/settings/shortcuts_settings.cpp - ../cockatrice/src/client/network/update/client/release_channel.cpp - ../cockatrice/src/interface/theme_manager.cpp - ../cockatrice/src/interface/widgets/quick_settings/settings_button_widget.cpp - ../cockatrice/src/interface/widgets/quick_settings/settings_popup_widget.cpp - ${VERSION_STRING_CPP} -) - -# ------------------------ -# Translations -# ------------------------ - -if(UPDATE_TRANSLATIONS) - file(GLOB_RECURSE translate_oracle_SRCS src/*.cpp src/*.h ../cockatrice/src/settingscache.cpp) - set(translate_SRCS ${translate_oracle_SRCS}) - set(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/oracle_en@source.ts") -else() - file(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") -endif(UPDATE_TRANSLATIONS) - -if(WIN32) - set(oracle_SOURCES ${oracle_SOURCES} oracle.rc) -endif(WIN32) - -if(APPLE) - set(MACOSX_BUNDLE_ICON_FILE appicon.icns) - set_source_files_properties( - ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources - ) - set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) -endif(APPLE) + ../cockatrice/src/carddatabase.cpp + ../cockatrice/src/pictureloader.cpp + ../cockatrice/src/settingscache.cpp + ../cockatrice/src/shortcutssettings.cpp + ../cockatrice/src/settings/carddatabasesettings.cpp + ../cockatrice/src/settings/serverssettings.cpp + ../cockatrice/src/settings/settingsmanager.cpp + ../cockatrice/src/settings/messagesettings.cpp + ../cockatrice/src/settings/gamefilterssettings.cpp + ../cockatrice/src/settings/layoutssettings.cpp + ../cockatrice/src/thememanager.cpp + ../cockatrice/src/qt-json/json.cpp + ) set(oracle_RESOURCES oracle.qrc) -# ------------------------ -# Qt resources -# ------------------------ -if(Qt6_FOUND) - qt6_add_resources(oracle_RESOURCES_RCC ${oracle_RESOURCES}) -elseif(Qt5_FOUND) - qt5_add_resources(oracle_RESOURCES_RCC ${oracle_RESOURCES}) -endif() +IF(UPDATE_TRANSLATIONS) + FILE(GLOB_RECURSE translate_oracle_SRCS ${CMAKE_SOURCE_DIR}/oracle/src/*.cpp ${CMAKE_SOURCE_DIR}/oracle/src/*.h) + SET(translate_SRCS ${translate_oracle_SRCS}) + SET(oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/oracle_en.ts") +ELSE() + FILE(GLOB oracle_TS "${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts") +ENDIF(UPDATE_TRANSLATIONS) -# ------------------------ -# Include directories -# ------------------------ -include_directories(../cockatrice/src) +if(WIN32) + set(oracle_SOURCES ${oracle_SOURCES} oracle.rc) +endif(WIN32) -# ------------------------ -# Optional libraries -# ------------------------ -# ZLIB -find_package(ZLIB) -if(ZLIB_FOUND) - include_directories(${ZLIB_INCLUDE_DIRS}) - add_definitions("-DHAS_ZLIB") - list(APPEND oracle_SOURCES src/zip/unzip.cpp src/zip/zipglobal.cpp) -else() - message(STATUS "Oracle: zlib not found; ZIP support disabled") -endif() - -# LZMA -find_package(LibLZMA) -if(LIBLZMA_FOUND) - include_directories(${LIBLZMA_INCLUDE_DIRS}) - add_definitions("-DHAS_LZMA") - list(APPEND oracle_SOURCES src/lzma/decompress.cpp) -else() - message(STATUS "Oracle: LibLZMA not found; xz support disabled") -endif() - -# ------------------------ -# Build executable -# ------------------------ - -set(ORACLE_MAC_QM_INSTALL_DIR "oracle.app/Contents/Resources/translations") -set(ORACLE_UNIX_QM_INSTALL_DIR "share/oracle/translations") -set(ORACLE_WIN32_QM_INSTALL_DIR "translations") - -if(Qt6_FOUND) - # Qt6 Translations are linked after the executable is created in manual mode - qt6_add_executable( - oracle - WIN32 - MACOSX_BUNDLE - ${oracle_SOURCES} - ${oracle_RESOURCES_RCC} - ${oracle_MOC_SRCS} - MANUAL_FINALIZATION - ) -elseif(Qt5_FOUND) - # Qt5 Translations need to be linked at executable creation time - if(Qt5LinguistTools_FOUND) - if(UPDATE_TRANSLATIONS) - qt5_create_translation(oracle_QM ${translate_SRCS} ${oracle_TS}) - else() - qt5_add_translation(oracle_QM ${oracle_TS}) - endif() - endif() - add_executable(oracle WIN32 MACOSX_BUNDLE ${oracle_MOC_SRCS} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_SOURCES}) - if(UNIX) - if(APPLE) - install(FILES ${oracle_QM} DESTINATION ${ORACLE_MAC_QM_INSTALL_DIR}) - else() - install(FILES ${oracle_QM} DESTINATION ${ORACLE_UNIX_QM_INSTALL_DIR}) - endif() - elseif(WIN32) - install(FILES ${oracle_QM} DESTINATION ${ORACLE_WIN32_QM_INSTALL_DIR}) - endif() -endif() - -# ------------------------ -# Link libraries -# ------------------------ -target_link_libraries( - oracle - PUBLIC libcockatrice_card - PUBLIC libcockatrice_settings - PUBLIC libcockatrice_network - PUBLIC ${ORACLE_QT_MODULES} -) - -if(ZLIB_FOUND) - target_link_libraries(oracle PUBLIC ${ZLIB_LIBRARIES}) -endif() - -if(LIBLZMA_FOUND) - target_link_libraries(oracle PUBLIC ${LIBLZMA_LIBRARIES}) -endif() - -# ------------------------ -# Install rules -# ------------------------ -if(UNIX) - if(APPLE) - set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") - set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") - set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) - set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) - set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) - set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) - - install(TARGETS oracle BUNDLE DESTINATION ./) - else() - # Assume linux - install(TARGETS oracle RUNTIME DESTINATION bin/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) - endif() -elseif(WIN32) - install(TARGETS oracle RUNTIME DESTINATION ./) -endif() - -if(NOT WIN32 AND NOT APPLE) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR}) -endif(NOT WIN32 AND NOT APPLE) - -# ------------------------ -# Qt plugin handling -# ------------------------ if(APPLE) - # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir oracle.app/Contents/Plugins) - set(qtconf_dest_dir oracle.app/Contents/Resources) + set(MACOSX_BUNDLE_ICON_FILE appicon.icns) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set(oracle_SOURCES ${oracle_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) +ENDIF(APPLE) - # Qt plugins: iconengines, platforms, styles, tls (Qt6) - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "*.dSYM" EXCLUDE - PATTERN "*_debug.dylib" EXCLUDE - PATTERN "iconengines/*.dylib" - PATTERN "platforms/*.dylib" - PATTERN "styles/*.dylib" - PATTERN "tls/*.dylib" - ) +set(ORACLE_LIBS) - install( - CODE " +# Qt4 stuff +if(Qt4_FOUND) + SET(QT_USE_QTNETWORK TRUE) + SET(QT_USE_QTSVG TRUE) + + # Include directories + INCLUDE(${QT_USE_FILE}) + include_directories(${QT_INCLUDES}) + LIST(APPEND ORACLE_LIBS ${QT_QTMAIN_LIBRARY}) + LIST(APPEND ORACLE_LIBS ${QT_LIBRARIES}) + + # Let cmake chew Qt4's translations and resource files + # Note: header files are MOC-ed automatically by cmake + IF(UPDATE_TRANSLATIONS) + QT4_CREATE_TRANSLATION(oracle_QM ${translate_SRCS} ${oracle_TS}) + ELSE(UPDATE_TRANSLATIONS) + QT4_ADD_TRANSLATION(oracle_QM ${oracle_TS}) + ENDIF(UPDATE_TRANSLATIONS) + + QT4_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) +endif() + +# qt5 stuff +if(Qt5Widgets_FOUND) + include_directories(${Qt5Widgets_INCLUDE_DIRS}) + list(APPEND ORACLE_LIBS Widgets) + + # QtConcurrent + find_package(Qt5Concurrent) + if(Qt5Concurrent_FOUND) + include_directories(${Qt5Concurrent_INCLUDE_DIRS}) + list(APPEND ORACLE_LIBS Concurrent) + endif() + + # QtNetwork + find_package(Qt5Network) + if(Qt5Network_FOUND) + include_directories(${Qt5Network_INCLUDE_DIRS}) + list(APPEND ORACLE_LIBS Network) + endif() + + # QtSvg + find_package(Qt5Svg) + if(Qt5Svg_FOUND) + include_directories(${Qt5Svg_INCLUDE_DIRS}) + list(APPEND ORACLE_LIBS Svg) + endif() + + # Let cmake chew Qt5's translations and resource files + # Note: header files are MOC-ed automatically by cmake + IF(UPDATE_TRANSLATIONS) + QT5_CREATE_TRANSLATION(oracle_QM ${translate_SRCS} ${oracle_TS}) + ELSE() + QT5_ADD_TRANSLATION(oracle_QM ${oracle_TS}) + ENDIF() + + QT5_ADD_RESOURCES(oracle_RESOURCES_RCC ${oracle_RESOURCES}) + + # guess plugins and libraries directory + set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins") + get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) + get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH) +endif() + +INCLUDE_DIRECTORIES(../cockatrice/src) + +# Libz is required to support zipped files +FIND_PACKAGE(ZLIB) +IF(ZLIB_FOUND) + INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) + ADD_DEFINITIONS("-DHAS_ZLIB") + + set(oracle_SOURCES ${oracle_SOURCES} + src/zip/unzip.cpp + src/zip/zipglobal.cpp + ) + +ELSE() + MESSAGE(STATUS "Oracle: zlib not found; ZIP support disabled") +ENDIF() + +# Build oracle binary and link it +ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_QM} ${oracle_RESOURCES_RCC} ${oracle_MOC_SRCS}) +set_property(TARGET oracle PROPERTY CXX_STANDARD 11) +set_property(TARGET oracle PROPERTY CXX_STANDARD_REQUIRED ON) + +if(Qt4_FOUND) + if(MSVC) + set(QT_USE_QTMAIN true) + endif() + TARGET_LINK_LIBRARIES(oracle ${ORACLE_LIBS}) +endif() +if(Qt5Widgets_FOUND) + if(MSVC) + TARGET_LINK_LIBRARIES(oracle Qt5::WinMain) + endif() + qt5_use_modules(oracle ${ORACLE_LIBS}) +endif() + +IF(ZLIB_FOUND) + TARGET_LINK_LIBRARIES(oracle ${ZLIB_LIBRARIES}) +ENDIF() + +if(UNIX) + if(APPLE) + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) + set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) + set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) + set_target_properties(oracle PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/Info.plist) + + INSTALL(TARGETS oracle BUNDLE DESTINATION ./) + INSTALL(FILES ${oracle_QM} DESTINATION ./oracle.app/Contents/Resources/translations) + else() + # Assume linux + INSTALL(TARGETS oracle RUNTIME DESTINATION bin/) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/oracle.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) + INSTALL(FILES ${oracle_QM} DESTINATION share/oracle/translations) + endif() +elseif(WIN32) + INSTALL(TARGETS oracle RUNTIME DESTINATION ./) + INSTALL(FILES ${oracle_QM} DESTINATION ./translations) +endif() + +IF (NOT WIN32 AND NOT APPLE) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR}) +ENDIF (NOT WIN32 AND NOT APPLE) + +if(APPLE) + # these needs to be relative to CMAKE_INSTALL_PREFIX + set(plugin_dest_dir oracle.app/Contents/Plugins) + set(qtconf_dest_dir oracle.app/Contents/Resources) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) + + # qt4: codecs, iconengines, imageformats + # qt5: iconengines, platforms + + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime + FILES_MATCHING REGEX "(codecs|iconengines|platforms)/.*\\.dylib" + REGEX ".*_debug\\.dylib" EXCLUDE) + + install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] Plugins = Plugins Translations = Resources/translations\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) - install( - CODE " + install(CODE " file(GLOB_RECURSE QTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/oracle.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR}\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) endif() -if(WIN32) - # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir Plugins) - set(qtconf_dest_dir .) - list(APPEND libSearchDirs ${QT_LIBRARY_DIR}) +IF(WIN32) + # these needs to be relative to CMAKE_INSTALL_PREFIX + set(plugin_dest_dir Plugins) + set(qtconf_dest_dir .) + list(APPEND libSearchDirs ${QT_LIBRARY_DIR}) + IF(ZLIB_FOUND) + # look for dll in the bin/ directory (gnuwin32 package) + get_filename_component(ZLIB_DLL_DIR "${ZLIB_INCLUDE_DIRS}/../bin/" REALPATH) + list(APPEND libSearchDirs ${ZLIB_DLL_DIR}) + # look for dll in the lib/ directory (nuget package) + get_filename_component(ZLIB_DLL_DIR "${ZLIB_LIBRARY}" DIRECTORY) + list(APPEND libSearchDirs ${ZLIB_DLL_DIR}) + ENDIF() - install( - DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" - DESTINATION ./ - FILES_MATCHING - PATTERN "*.dll" - ) + # qt4: codecs, iconengines, imageformats + # qt5: iconengines, imageformats, platforms - # Qt plugins: iconengines, platforms, styles, tls (Qt6) - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "iconengines/qsvgicon.dll" - PATTERN "platforms/qdirect2d.dll" - PATTERN "platforms/qminimal.dll" - PATTERN "platforms/qoffscreen.dll" - PATTERN "platforms/qwindows.dll" - PATTERN "styles/qcertonlybackend.dll" - PATTERN "styles/qopensslbackend.dll" - PATTERN "styles/qschannelbackend.dll" - PATTERN "styles/qwindowsvistastyle.dll" - PATTERN "tls/qcertonlybackend.dll" - PATTERN "tls/qopensslbackend.dll" - PATTERN "tls/qschannelbackend.dll" - ) + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime + FILES_MATCHING REGEX "(codecs|iconengines|platforms)/.*[^d]\\.dll") - install( - CODE " + install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] Plugins = Plugins Translations = Resources/translations\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) - install( - CODE " + install(CODE " file(GLOB_RECURSE QTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\") - " - COMPONENT Runtime - ) -endif() - -# ------------------------ -# Qt translations -# ------------------------ -if(Qt6_FOUND AND Qt6LinguistTools_FOUND) - #Qt6 Translations happen after the executable is built up - if(UPDATE_TRANSLATIONS) - qt6_add_translations( - oracle - TS_FILES - ${oracle_TS} - SOURCES - ${translate_SRCS} - QM_FILES_OUTPUT_VARIABLE - oracle_QM - ) - else() - qt6_add_translations(oracle TS_FILES ${oracle_TS} QM_FILES_OUTPUT_VARIABLE oracle_QM) - endif() - - if(UNIX) - if(APPLE) - install(FILES ${oracle_QM} DESTINATION ${ORACLE_MAC_QM_INSTALL_DIR}) - else() - install(FILES ${oracle_QM} DESTINATION ${ORACLE_UNIX_QM_INSTALL_DIR}) - endif() - elseif(WIN32) - install(FILES ${oracle_QM} DESTINATION ${ORACLE_WIN32_QM_INSTALL_DIR}) - endif() -endif() - -if(Qt6_FOUND) - qt6_finalize_target(oracle) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/oracle.exe\" \"\${QTPLUGINS}\" \"${libSearchDirs}\") + " COMPONENT Runtime) +endif() +#Compile a portable version, default off +option(PORTABLE "portable build" OFF) +IF(PORTABLE) +add_definitions(-DPORTABLE_BUILD) endif() diff --git a/oracle/oracle_en@source.ts b/oracle/oracle_en@source.ts deleted file mode 100644 index 943b44a97..000000000 --- a/oracle/oracle_en@source.ts +++ /dev/null @@ -1,610 +0,0 @@ - - - - - IntroPage - - - Introduction - - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - - - - - Interface language: - - - - - Version: - - - - - LoadSetsPage - - - Source selection - - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - Load sets file - - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - - - - - The provided URL is not valid. - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - Parsing file - - - - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - - Failed to open Zip archive: %1. - - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - - - - - Zip extraction failed: %1. - - - - - Sorry, this version of Oracle does not support zipped files. - - - - - Failed to interpret downloaded data. - - - - - Do you want to download the uncompressed file instead? - - - - - The file was retrieved successfully, but it does not contain any sets data. - - - - - LoadSpoilersPage - - - Save spoiler database - - - - - XML; spoiler database (*.xml) - - - - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - LoadTokensPage - - - Save token database - - - - - XML; token database (*.xml) - - - - - tokens - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - OracleImporter - - - Dummy set containing tokens - - - - - OracleWizard - - - Oracle Importer - - - - - OutroPage - - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - - - - - SaveSetsPage - - - - Error - - - - - No set has been imported. - - - - - Sets imported - - - - - A cockatrice database file of %1 MB has been downloaded. - - - - - The following sets have been found: - - - - - Press "Save" to store the imported cards in the Cockatrice database. - - - - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - - - - - %1: %2 cards imported - - - - - Save card database - - - - - XML; card database (*.xml) - - - - - The file could not be saved to %1 - - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - - - - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - The file could not be saved to %1 - - - - - UnZip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - - - - - Partially corrupted archive. Some files might be extracted. - - - - - Corrupted archive. - - - - - Wrong password. - - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - - - - - File write error. - - - - - File seek error. - - - - - Unable to create a directory. - - - - - Invalid device. - - - - - Invalid or incompatible zip archive. - - - - - Inconsistent headers. Archive might be corrupted. - - - - - Unknown error. - - - - - Zip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - - - - - File write error. - - - - - File seek error. - - - - - Unknown error. - - - - - i18n - - - English - - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - - diff --git a/oracle/resources/appicon.icns b/oracle/resources/appicon.icns index fbbee56c8..163004558 100644 Binary files a/oracle/resources/appicon.icns and b/oracle/resources/appicon.icns differ diff --git a/oracle/src/lzma/decompress.cpp b/oracle/src/lzma/decompress.cpp deleted file mode 100644 index 718cde207..000000000 --- a/oracle/src/lzma/decompress.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Simple routing to extract a single file from a xz archive - * Heavily based from doc/examples/02_decompress.c obtained from - * the official xz git repository: git.tukaani.org/xz.git - * The license from the original file header follows - * - * Author: Lasse Collin - * This file has been put into the public domain. - * You can do whatever you want with this file. - */ - - -#include -#include - -#include "decompress.h" - -XzDecompressor::XzDecompressor(QObject *parent) - : QObject(parent) -{ - -} - -bool XzDecompressor::decompress(QBuffer *in, QBuffer *out) -{ - lzma_stream strm = LZMA_STREAM_INIT; - bool success; - - if (!init_decoder(&strm)) { - return false; - } - - success = internal_decompress(&strm, in, out); - - // Free the memory allocated for the decoder. This only needs to be - // done after the last file. - lzma_end(&strm); - - return success; -} - -bool XzDecompressor::init_decoder(lzma_stream *strm) -{ - // Initialize a .xz decoder. The decoder supports a memory usage limit - // and a set of flags. - // - // The memory usage of the decompressor depends on the settings used - // to compress a .xz file. It can vary from less than a megabyte to - // a few gigabytes, but in practice (at least for now) it rarely - // exceeds 65 MiB because that's how much memory is required to - // decompress files created with "xz -9". Settings requiring more - // memory take extra effort to use and don't (at least for now) - // provide significantly better compression in most cases. - // - // Memory usage limit is useful if it is important that the - // decompressor won't consume gigabytes of memory. The need - // for limiting depends on the application. In this example, - // no memory usage limiting is used. This is done by setting - // the limit to UINT64_MAX. - // - // The .xz format allows concatenating compressed files as is: - // - // echo foo | xz > foobar.xz - // echo bar | xz >> foobar.xz - // - // When decompressing normal standalone .xz files, LZMA_CONCATENATED - // should always be used to support decompression of concatenated - // .xz files. If LZMA_CONCATENATED isn't used, the decoder will stop - // after the first .xz stream. This can be useful when .xz data has - // been embedded inside another file format. - // - // Flags other than LZMA_CONCATENATED are supported too, and can - // be combined with bitwise-or. See lzma/container.h - // (src/liblzma/api/lzma/container.h in the source package or e.g. - // /usr/include/lzma/container.h depending on the install prefix) - // for details. - lzma_ret ret = lzma_stream_decoder( - strm, UINT64_MAX, LZMA_CONCATENATED); - - // Return successfully if the initialization went fine. - if (ret == LZMA_OK) - return true; - - // Something went wrong. The possible errors are documented in - // lzma/container.h (src/liblzma/api/lzma/container.h in the source - // package or e.g. /usr/include/lzma/container.h depending on the - // install prefix). - // - // Note that LZMA_MEMLIMIT_ERROR is never possible here. If you - // specify a very tiny limit, the error will be delayed until - // the first headers have been parsed by a call to lzma_code(). - const char *msg; - switch (ret) { - case LZMA_MEM_ERROR: - msg = "Memory allocation failed"; - break; - - case LZMA_OPTIONS_ERROR: - msg = "Unsupported decompressor flags"; - break; - - default: - // This is most likely LZMA_PROG_ERROR indicating a bug in - // this program or in liblzma. It is inconvenient to have a - // separate error message for errors that should be impossible - // to occur, but knowing the error code is important for - // debugging. That's why it is good to print the error code - // at least when there is no good error message to show. - msg = "Unknown error, possibly a bug"; - break; - } - - qDebug() << "Error initializing the decoder:" << msg << "(error code " << ret << ")"; - return false; -} - - -bool XzDecompressor::internal_decompress(lzma_stream *strm, QBuffer *in, QBuffer *out) -{ - // When LZMA_CONCATENATED flag was used when initializing the decoder, - // we need to tell lzma_code() when there will be no more input. - // This is done by setting action to LZMA_FINISH instead of LZMA_RUN - // in the same way as it is done when encoding. - // - // When LZMA_CONCATENATED isn't used, there is no need to use - // LZMA_FINISH to tell when all the input has been read, but it - // is still OK to use it if you want. When LZMA_CONCATENATED isn't - // used, the decoder will stop after the first .xz stream. In that - // case some unused data may be left in strm->next_in. - lzma_action action = LZMA_RUN; - - uint8_t inbuf[BUFSIZ]; - uint8_t outbuf[BUFSIZ]; - qint64 bytesAvailable; - - strm->next_in = NULL; - strm->avail_in = 0; - strm->next_out = outbuf; - strm->avail_out = sizeof(outbuf); - while (true) { - if (strm->avail_in == 0) { - strm->next_in = inbuf; - bytesAvailable = in->bytesAvailable(); - if(bytesAvailable == 0) { - // Once the end of the input file has been reached, - // we need to tell lzma_code() that no more input - // will be coming. As said before, this isn't required - // if the LZMA_CONCATENATED flag isn't used when - // initializing the decoder. - action = LZMA_FINISH; - } else if(bytesAvailable >= BUFSIZ) { - in->read((char*) inbuf, BUFSIZ); - strm->avail_in = BUFSIZ; - } else { - in->read((char*) inbuf, bytesAvailable); - strm->avail_in = bytesAvailable; - } - } - - lzma_ret ret = lzma_code(strm, action); - - if (strm->avail_out == 0 || ret == LZMA_STREAM_END) { - qint64 write_size = sizeof(outbuf) - strm->avail_out; - - if (out->write((char *) outbuf, write_size) != write_size) { - qDebug() << "Write error"; - return false; - } - - strm->next_out = outbuf; - strm->avail_out = sizeof(outbuf); - } - - if (ret != LZMA_OK) { - // Once everything has been decoded successfully, the - // return value of lzma_code() will be LZMA_STREAM_END. - // - // It is important to check for LZMA_STREAM_END. Do not - // assume that getting ret != LZMA_OK would mean that - // everything has gone well or that when you aren't - // getting more output it must have successfully - // decoded everything. - if (ret == LZMA_STREAM_END) - return true; - - // It's not LZMA_OK nor LZMA_STREAM_END, - // so it must be an error code. See lzma/base.h - // (src/liblzma/api/lzma/base.h in the source package - // or e.g. /usr/include/lzma/base.h depending on the - // install prefix) for the list and documentation of - // possible values. Many values listen in lzma_ret - // enumeration aren't possible in this example, but - // can be made possible by enabling memory usage limit - // or adding flags to the decoder initialization. - const char *msg; - switch (ret) { - case LZMA_MEM_ERROR: - msg = "Memory allocation failed"; - break; - - case LZMA_FORMAT_ERROR: - // .xz magic bytes weren't found. - msg = "The input is not in the .xz format"; - break; - - case LZMA_OPTIONS_ERROR: - // For example, the headers specify a filter - // that isn't supported by this liblzma - // version (or it hasn't been enabled when - // building liblzma, but no-one sane does - // that unless building liblzma for an - // embedded system). Upgrading to a newer - // liblzma might help. - // - // Note that it is unlikely that the file has - // accidentally became corrupt if you get this - // error. The integrity of the .xz headers is - // always verified with a CRC32, so - // unintentionally corrupt files can be - // distinguished from unsupported files. - msg = "Unsupported compression options"; - break; - - case LZMA_DATA_ERROR: - msg = "Compressed file is corrupt"; - break; - - case LZMA_BUF_ERROR: - // Typically this error means that a valid - // file has got truncated, but it might also - // be a damaged part in the file that makes - // the decoder think the file is truncated. - // If you prefer, you can use the same error - // message for this as for LZMA_DATA_ERROR. - msg = "Compressed file is truncated or " - "otherwise corrupt"; - break; - - default: - // This is most likely LZMA_PROG_ERROR. - msg = "Unknown error, possibly a bug"; - break; - } - - qDebug() << "Decoder error:" << msg << "(error code " << ret << ")"; - return false; - } - } -} - diff --git a/oracle/src/lzma/decompress.h b/oracle/src/lzma/decompress.h deleted file mode 100644 index f0e315f8b..000000000 --- a/oracle/src/lzma/decompress.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef XZ_DECOMPRESS_H -#define XZ_DECOMPRESS_H - -#include -#include - -class XzDecompressor : public QObject -{ - Q_OBJECT -public: - XzDecompressor(QObject *parent = 0); - ~XzDecompressor() { }; - bool decompress(QBuffer *in, QBuffer *out); -private: - bool init_decoder(lzma_stream *strm); - bool internal_decompress(lzma_stream *strm, QBuffer *in, QBuffer *out); -}; - -#endif diff --git a/oracle/src/main.cpp b/oracle/src/main.cpp index 5def0c887..5a6e03672 100644 --- a/oracle/src/main.cpp +++ b/oracle/src/main.cpp @@ -1,98 +1,66 @@ -#include "main.h" - -#include "interface/theme_manager.h" -#include "oraclewizard.h" - -#include <../../cockatrice/src/client/settings/cache_settings.h> #include -#include +#include #include -#include -#include #include +#include + +#include "main.h" +#include "oraclewizard.h" +#include "settingscache.h" +#include "thememanager.h" QTranslator *translator, *qtTranslator; +SettingsCache *settingsCache; ThemeManager *themeManager; const QString translationPrefix = "oracle"; QString translationPath; -bool isSpoilersOnly; -bool isBackgrounded; void installNewTranslator() { - QString lang = SettingsCache::instance().getLang(); + QString lang = settingsCache->getLang(); - QString qtNameHint = "qt_" + lang; -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QString qtTranslationPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); -#else - QString qtTranslationPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); -#endif - - bool qtTranslationLoaded = qtTranslator->load(qtNameHint, qtTranslationPath); - if (!qtTranslationLoaded) { - qDebug() << "Unable to load qt translation" << qtNameHint << "at" << qtTranslationPath; - } else { - qDebug() << "Loaded qt translation" << qtNameHint << "at" << qtTranslationPath; - } + qtTranslator->load("qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); qApp->installTranslator(qtTranslator); - - QString appNameHint = translationPrefix + "_" + lang; - bool appTranslationLoaded = qtTranslator->load(appNameHint, translationPath); - if (!appTranslationLoaded) { - qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; - } else { - qDebug() << "Loaded" << translationPrefix << "translation" << appNameHint << "at" << translationPath; - } + translator->load(translationPrefix + "_" + lang, translationPath); qApp->installTranslator(translator); } int main(int argc, char *argv[]) { - QApplication app(argc, argv); + QApplication app(argc, argv); - QCoreApplication::setOrganizationName("Cockatrice"); - QCoreApplication::setOrganizationDomain("cockatrice"); - // this can't be changed, as it influences the default save path for cards.xml - QCoreApplication::setApplicationName("Cockatrice"); +#if QT_VERSION < 0x050000 + // gone in Qt5, all source files _MUST_ be utf8-encoded + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); +#endif - // If the program is opened with the -s flag, it will only do spoilers. Otherwise it will do MTGJSON/Tokens - QCommandLineParser parser; - QCommandLineOption spoilersOnlyOption("s", QCoreApplication::translate("main", "Only run in spoiler mode")); - QCommandLineOption backgroundOption("b", QCoreApplication::translate("main", "Run in no-confirm background mode")); - parser.addOption(spoilersOnlyOption); - parser.addOption(backgroundOption); - parser.process(app); - isSpoilersOnly = parser.isSet(spoilersOnlyOption); - isBackgrounded = parser.isSet(backgroundOption); + QCoreApplication::setOrganizationName("Cockatrice"); + QCoreApplication::setOrganizationDomain("cockatrice"); + // this can't be changed, as it influences the default savepath for cards.xml + QCoreApplication::setApplicationName("Cockatrice"); #ifdef Q_OS_MAC translationPath = qApp->applicationDirPath() + "/../Resources/translations"; #elif defined(Q_OS_WIN) translationPath = qApp->applicationDirPath() + "/translations"; #else // linux - translationPath = qApp->applicationDirPath() + "/../share/oracle/translations"; + translationPath = qApp->applicationDirPath() + "/../share/cockatrice/translations"; #endif + settingsCache = new SettingsCache; themeManager = new ThemeManager; qtTranslator = new QTranslator; translator = new QTranslator; installNewTranslator(); - OracleWizard wizard; + OracleWizard wizard; QIcon icon("theme:appicon.svg"); wizard.setWindowIcon(icon); - // set name of the app desktop file; used by wayland to load the window icon - QGuiApplication::setDesktopFileName("oracle"); - wizard.show(); + wizard.show(); - if (isBackgrounded) { - QTimer::singleShot(0, &wizard, [&wizard]() { wizard.runInBackground(); }); - } - - return app.exec(); + return app.exec(); } diff --git a/oracle/src/main.h b/oracle/src/main.h index 2541b68b8..e4b063eb5 100644 --- a/oracle/src/main.h +++ b/oracle/src/main.h @@ -2,12 +2,10 @@ #define MAIN_H class QTranslator; -class QString; extern QTranslator *translator; extern const QString translationPrefix; extern QString translationPath; -extern bool isSpoilersOnly; void installNewTranslator(); diff --git a/oracle/src/oracleimporter.cpp b/oracle/src/oracleimporter.cpp index 578afd98d..f4f8e8705 100644 --- a/oracle/src/oracleimporter.cpp +++ b/oracle/src/oracleimporter.cpp @@ -1,575 +1,327 @@ #include "oracleimporter.h" +#if QT_VERSION < 0x050000 + #include +#else + #include +#endif +#include -#include "libcockatrice/interfaces/noop_card_preference_provider.h" -#include "libcockatrice/interfaces/noop_card_set_priority_controller.h" -#include "parsehelpers.h" #include "qt-json/json.h" -#include -#include -#include -#include -#include -#include - -static const QList kConstructedCounts = {{4, "legal"}, {0, "banned"}}; - -static const QList kSingletonCounts = {{1, "legal"}, {0, "banned"}}; - -SplitCardPart::SplitCardPart(const QString &_name, - const QString &_text, - const QVariantHash &_properties, - const PrintingInfo &_printingInfo) - : name(_name), text(_text), properties(_properties), printingInfo(_printingInfo) +OracleImporter::OracleImporter(const QString &_dataDir, QObject *parent) + : CardDatabase(parent), dataDir(_dataDir) { } -const QRegularExpression OracleImporter::formatRegex = QRegularExpression("^format-"); - -OracleImporter::OracleImporter(QObject *parent) : QObject(parent) -{ -} - -static CardSet::Priority getSetPriority(const QString &setType, const QString &shortName) -{ - if (!setTypePriorities.contains(setType.toLower())) { - qDebug() << "warning: Set type" << setType << "unrecognized for prioritization"; - } - CardSet::Priority priority = setTypePriorities.value(setType.toLower(), CardSet::PriorityOther); - if (nonEnglishSets.contains(shortName)) { - priority = CardSet::PriorityLowest; - } - return priority; -} - bool OracleImporter::readSetsFromByteArray(const QByteArray &data) { + QList newSetList; + bool ok; - auto setsMap = QtJson::Json::parse(QString(data), ok).toMap().value("data").toMap(); + setsMap = QtJson::Json::parse(QString(data), ok).toMap(); if (!ok) { qDebug() << "error: QtJson::Json::parse()"; - return false; + return 0; } + + QListIterator it(setsMap.values()); + QVariantMap map; - QList newSetList; - - QListIterator it(setsMap.values()); + QString edition; + QString editionLong; + QVariant editionCards; + QString setType; + QDate releaseDate; while (it.hasNext()) { - QVariantMap map = it.next().toMap(); - QString shortName = map.value("code").toString().toUpper(); - QString longName = map.value("name").toString(); - QList setCards = map.value("cards").toList(); - QString setType = map.value("type").toString(); - QDate releaseDate = map.value("releaseDate").toDate(); - CardSet::Priority priority = getSetPriority(setType, shortName); + map = it.next().toMap(); + edition = map.value("code").toString(); + editionLong = map.value("name").toString(); + editionCards = map.value("cards"); + setType = map.value("type").toString(); // capitalize set type - if (setType.length() > 0) { - // basic grammar for words that aren't capitalized, like in "From the Vault" - const QStringList noCapitalize = {"the", "a", "an", "on", "to", "for", "of", "in", "and", "with", "or"}; - QStringList words = setType.split("_"); - setType.clear(); - bool first = false; - for (auto &item : words) { - if (first && noCapitalize.contains(item)) { - setType += item + QString(" "); - } else { - setType += item[0].toUpper() + item.mid(1, -1) + QString(" "); - first = true; - } - } - setType = setType.trimmed(); - } - newSetList.append(SetToDownload(shortName, longName, setCards, priority, setType, releaseDate)); + if(setType.length() > 0) + setType[0] = setType[0].toUpper(); + releaseDate = map.value("releaseDate").toDate(); + + newSetList.append(SetToDownload(edition, editionLong, editionCards, setType, releaseDate)); } - std::sort(newSetList.begin(), newSetList.end()); + qSort(newSetList); - if (newSetList.isEmpty()) { + if (newSetList.isEmpty()) return false; - } allSets = newSetList; return true; } -static QString getMainCardType(const QStringList &typeList) +CardInfo *OracleImporter::addCard(const QString &setName, + QString cardName, + bool isToken, + int cardId, + QString &cardCost, + QString &cmc, + const QString &cardType, + const QString &cardPT, + int cardLoyalty, + const QString &cardText, + const QStringList & colors, + const QStringList & relatedCards, + const QStringList & reverseRelatedCards, + bool upsideDown + ) { - if (typeList.isEmpty()) { - return {}; - } + QStringList cardTextRows = cardText.split("\n"); - static const QStringList typePriority = {"Planeswalker", "Creature", "Land", "Sorcery", - "Instant", "Artifact", "Enchantment"}; - - for (const auto &type : typePriority) { - if (typeList.contains(type)) { - return type; - } - } - - return typeList.first(); -} - -/** - * Sorts and deduplicates the color chars in the string by WUBRG order. - * - * @param colors The string containing the color chars. Will be modified in-place - */ -static void sortAndReduceColors(QString &colors) -{ - // sort - static const QHash colorOrder{{'W', 0}, {'U', 1}, {'B', 2}, {'R', 3}, {'G', 4}}; - std::sort(colors.begin(), colors.end(), - [](const QChar a, const QChar b) { return colorOrder.value(a, INT_MAX) < colorOrder.value(b, INT_MAX); }); - // reduce - QChar lastChar = '\0'; - for (int i = 0; i < colors.size(); ++i) { - if (colors.at(i) == lastChar) - colors.remove(i, 1); - else - lastChar = colors.at(i); - } -} - -CardInfoPtr OracleImporter::addCard(QString name, - const QString &text, - bool isToken, - QVariantHash properties, - const QList &relatedCards, - const PrintingInfo &printingInfo) -{ // Workaround for card name weirdness - name = name.replace("Æ", "AE"); - name = name.replace("’", "'"); - if (cards.contains(name)) { - CardInfoPtr card = cards.value(name); - card->addToSet(printingInfo.getSet(), printingInfo); - if (card->getProperties().filter(formatRegex).empty()) { - card->combineLegalities(properties); - } - return card; + cardName = cardName.replace("Æ", "AE"); + cardName = cardName.replace("’", "'"); + + CardInfo * card; + if (cards.contains(cardName)) { + card = cards.value(cardName); + } else { + // Remove {} around mana costs + cardCost.remove(QChar('{')); + cardCost.remove(QChar('}')); + + // detect mana generator artifacts + bool mArtifact = false; + if (cardType.endsWith("Artifact")) + for (int i = 0; i < cardTextRows.size(); ++i) + if (cardTextRows[i].contains("{T}") && cardTextRows[i].contains("to your mana pool")) + mArtifact = true; + + // detect cards that enter the field tapped + bool cipt = cardText.contains("Hideaway") || (cardText.contains(cardName + " enters the battlefield tapped") && !cardText.contains(cardName + " enters the battlefield tapped unless")); + + // insert the card and its properties + card = new CardInfo(cardName, isToken, cardCost, cmc, cardType, cardPT, cardText, colors, relatedCards, reverseRelatedCards, upsideDown, cardLoyalty, cipt); + int tableRow = 1; + QString mainCardType = card->getMainCardType(); + if ((mainCardType == "Land") || mArtifact) + tableRow = 0; + else if ((mainCardType == "Sorcery") || (mainCardType == "Instant")) + tableRow = 3; + else if (mainCardType == "Creature") + tableRow = 2; + card->setTableRow(tableRow); + + cards.insert(cardName, card); } + card->setMuId(setName, cardId); - // Remove {} around mana costs, except if it's split cost - QString manacost = properties.value("manacost").toString(); - if (!manacost.isEmpty()) { - QStringList symbols = manacost.split("}"); - QString formattedCardCost; - for (QString symbol : symbols) { - static const auto manaCostPattern = QRegularExpression("[0-9WUBGRP]/[0-9WUBGRP]"); - if (symbol.contains(manaCostPattern)) { - symbol.append("}"); - } else { - symbol.remove(QChar('{')); - } - formattedCardCost.append(symbol); - } - properties.insert("manacost", formattedCardCost); - } - - // fix colors - QString allColors = properties.value("colors").toString(); - if (allColors.size() > 1) { - sortAndReduceColors(allColors); - properties.insert("colors", allColors); - } - QString allColorIdent = properties.value("coloridentity").toString(); - if (allColorIdent.size() > 1) { - sortAndReduceColors(allColorIdent); - properties.insert("coloridentity", allColorIdent); - } - - // DETECT CARD POSITIONING INFO - - bool landscapeOrientation = properties.value("maintype").toString() == "Battle" || - properties.value("layout").toString() == "split" || - properties.value("layout").toString() == "planar"; - - // cards that enter the field tapped - bool cipt = parseCipt(name, text) || landscapeOrientation; - - // table row - int tableRow = 1; - QString mainCardType = properties.value("maintype").toString(); - if (mainCardType == "Land") - tableRow = 0; - else if (mainCardType == "Sorcery" || mainCardType == "Instant") - tableRow = 3; - else if (mainCardType == "Creature") - tableRow = 2; - - // card side - QString side = properties.value("side").toString() == "b" ? "back" : "front"; - properties.insert("side", side); - - // upsideDown (flip cards) - QString layout = properties.value("layout").toString(); - bool upsideDown = layout == "flip" && side == "back"; - - // insert the card and its properties - SetToPrintingsMap setsInfo; - setsInfo[printingInfo.getSet()->getShortName()].append(printingInfo); - CardInfo::UiAttributes attributes = {cipt, landscapeOrientation, tableRow, upsideDown}; - CardInfoPtr newCard = - CardInfo::newInstance(name, text, isToken, properties, relatedCards, {}, setsInfo, attributes); - - if (name.isEmpty()) { - qDebug() << "warning: an empty card was added to set" << printingInfo.getSet()->getShortName(); - } - cards.insert(name, newCard); - - return newCard; + return card; } -static QString getStringPropertyFromMap(const QVariantMap &card, const QString &propertyName) +void OracleImporter::extractColors(const QStringList & in, QStringList & out) { - return card.contains(propertyName) ? card.value(propertyName).toString() : QString(""); + foreach(QString c, in) + { + if (c == "White") + out << "W"; + else if (c == "Blue") + out << "U"; + else if (c == "Black") + out << "B"; + else if (c == "Red") + out << "R"; + else if (c == "Green") + out << "G"; + else + qDebug() << "error: unknown color:" << c; + } } -int OracleImporter::importCardsFromSet(const CardSetPtr ¤tSet, const QList &cardsList) +int OracleImporter::importTextSpoiler(CardSet *set, const QVariant &data) { - // mtgjson name => xml name - static const QMap cardProperties{ - {"manaCost", "manacost"}, {"manaValue", "cmc"}, {"type", "type"}, - {"loyalty", "loyalty"}, {"layout", "layout"}, {"side", "side"}, - {"convertedManaCost", "cmc"}, // old name for manaValue, for backwards compatibility - }; + int cards = 0; + + QListIterator it(data.toList()); + QVariantMap map; + QString cardName; + QString cardCost; + QString cmc; + QString cardType; + QString cardPT; + QString cardText; + QStringList colors; + QStringList relatedCards; + QStringList reverseRelatedCards; // dummy + int cardId; + int cardLoyalty; + bool upsideDown = false; + QMap splitCards; - // mtgjson name => xml name - static const QMap setInfoProperties{ - {"number", "num"}, {"rarity", "rarity"}, {"isOnlineOnly", "isOnlineOnly"}, {"isRebalanced", "isRebalanced"}}; + while (it.hasNext()) + { + map = it.next().toMap(); - // mtgjson name => xml name - static const QMap identifierProperties{{"multiverseId", "muid"}, {"scryfallId", "uuid"}}; + QString layout = map.value("layout").toString(); - static const QString ptSeparator = "/"; - static constexpr bool isToken = false; - static const QList setsWithCardsWithSameNameButDifferentText = {"UST"}; + if(layout == "token") + continue; - int numCards = 0; - - // Keeps track of any split card faces encountered so far - QMap, QString>> splitCards; - - // Keeps track of all names encountered so far - QList allNameProps; - - for (const QVariant &cardVar : cardsList) { - QVariantMap card = cardVar.toMap(); - - /* Currently used layouts are: - * augment, double_faced_token, flip, host, leveler, meld, normal, planar, - * saga, scheme, split, token, transform, vanguard - */ - QString layout = getStringPropertyFromMap(card, "layout"); - - // don't import tokens from the json file - if (layout == "token") { + if(layout == "split") + { + // Enqueue split card for later handling + cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; + if (cardId) + splitCards.insertMulti(cardId, map); continue; } // normal cards handling - QString name = getStringPropertyFromMap(card, "name"); - QString text = getStringPropertyFromMap(card, "text"); - QString faceName = getStringPropertyFromMap(card, "faceName"); - if (faceName.isEmpty()) { - faceName = name; - } + cardName = map.contains("name") ? map.value("name").toString() : QString(""); + cardCost = map.contains("manaCost") ? map.value("manaCost").toString() : QString(""); + cmc = map.contains("cmc") ? map.value("cmc").toString() : QString("0"); + cardType = map.contains("type") ? map.value("type").toString() : QString(""); + cardPT = map.contains("power") || map.contains("toughness") ? map.value("power").toString() + QString('/') + map.value("toughness").toString() : QString(""); + cardText = map.contains("text") ? map.value("text").toString() : QString(""); + cardId = map.contains("multiverseid") ? map.value("multiverseid").toInt() : 0; + cardLoyalty = map.contains("loyalty") ? map.value("loyalty").toInt() : 0; + relatedCards = map.contains("names") ? map.value("names").toStringList() : QStringList(); + relatedCards.removeAll(cardName); - // card properties - QVariantHash properties; - for (auto i = cardProperties.cbegin(), end = cardProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card, mtgjsonProperty); - if (!propertyValue.isEmpty()) - properties.insert(xmlPropertyName, propertyValue); - } - - // per-set properties - PrintingInfo printingInfo = PrintingInfo(currentSet); - for (auto i = setInfoProperties.cbegin(), end = setInfoProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card, mtgjsonProperty); - if (!propertyValue.isEmpty()) - printingInfo.setProperty(xmlPropertyName, propertyValue); - } - - // handle flavorNames specially due to double-faced cards - QString faceFlavorName = getStringPropertyFromMap(card, "faceFlavorName"); - QString flavorName = !faceFlavorName.isEmpty() ? faceFlavorName : getStringPropertyFromMap(card, "flavorName"); - if (!flavorName.isEmpty()) { - printingInfo.setProperty("flavorName", flavorName); - } - - // Identifiers - for (auto i = identifierProperties.cbegin(), end = identifierProperties.cend(); i != end; ++i) { - QString mtgjsonProperty = i.key(); - QString xmlPropertyName = i.value(); - QString propertyValue = getStringPropertyFromMap(card.value("identifiers").toMap(), mtgjsonProperty); - if (!propertyValue.isEmpty()) { - printingInfo.setProperty(xmlPropertyName, propertyValue); - } - } - - QString numComponent; - const QString numProperty = printingInfo.getProperty("num"); - const QChar lastChar = numProperty.isEmpty() ? QChar() : numProperty.back(); - - // Un-Sets do some wonky stuff. Split up these cards as individual entries. - // these cards will have a num with a letter (abc) behind it, put that letter into the name - if (setsWithCardsWithSameNameButDifferentText.contains(currentSet->getShortName()) && - allNameProps.contains(faceName) && layout == "normal" && lastChar.isLetter()) { - numComponent = " (" + QString(lastChar).toLower() + ")"; - } - allNameProps.append(faceName); - - // special handling properties - QString colors = card.value("colors").toStringList().join(""); - if (!colors.isEmpty()) { - properties.insert("colors", colors); - } - - // special handling properties - QString colorIdentity = card.value("colorIdentity").toStringList().join(""); - if (!colorIdentity.isEmpty()) { - properties.insert("coloridentity", colorIdentity); - } - - const auto &mainCardType = getMainCardType(card.value("types").toStringList()); - if (mainCardType.isEmpty()) { - qDebug() << "warning: no mainCardType for card:" << name; + if(0 == QString::compare(map.value("layout").toString(), QString("flip"), Qt::CaseInsensitive)) + { + QStringList cardNames = map.contains("names") ? map.value("names").toStringList() : QStringList(); + upsideDown = (cardNames.indexOf(cardName) > 0); } else { - properties.insert("maintype", mainCardType); + upsideDown = false; } - // Depending on whether power and/or toughness are present, the format - // is either P/T (most common), P (no toughness), or /T (no power). - QString power = getStringPropertyFromMap(card, "power"); - QString toughness = getStringPropertyFromMap(card, "toughness"); - if (toughness.isEmpty() && !power.isEmpty()) { - properties.insert("pt", power); - } else if (!toughness.isEmpty()) { - properties.insert("pt", power + ptSeparator + toughness); - } + colors.clear(); + extractColors(map.value("colors").toStringList(), colors); - auto legalities = card.value("legalities").toMap(); - for (auto i = legalities.cbegin(), end = legalities.cend(); i != end; ++i) { - properties.insert(QString("format-%1").arg(i.key()), i.value().toString().toLower()); - } + CardInfo *card = addCard(set->getShortName(), cardName, false, cardId, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, reverseRelatedCards, upsideDown); - // split cards are considered a single card, enqueue for later merging - if (layout == "split" || layout == "aftermath" || layout == "adventure" || layout == "prepare") { - auto _faceName = getStringPropertyFromMap(card, "faceName"); - SplitCardPart split(_faceName, text, properties, printingInfo); - auto found_iter = splitCards.find(name + numProperty); - if (found_iter == splitCards.end()) { - splitCards.insert(name + numProperty, {{split}, name}); - } else { - found_iter->first.append(split); - } - } else { - // relations - QList relatedCards; - - // add other face for split cards as card relation - if (!getStringPropertyFromMap(card, "side").isEmpty()) { - auto faceManaValue = getStringPropertyFromMap(card, "faceManaValue"); - if (faceManaValue.isEmpty()) { - // check the old name for the property, for backwards compatibility purposes - faceManaValue = getStringPropertyFromMap(card, "faceConvertedManaCost"); - } - properties["cmc"] = faceManaValue; - - if (layout == "meld") { // meld cards don't work - static const QRegularExpression meldNameRegex{"then meld them into ([^\\.]*)"}; - QString additionalName = meldNameRegex.match(text).captured(1); - if (!additionalName.isNull()) { - relatedCards.append(new CardRelation(additionalName, CardRelationType::TransformInto)); - } - } else { - for (const QString &additionalName : name.split(" // ")) { - if (additionalName != faceName) { - relatedCards.append(new CardRelation(additionalName, CardRelationType::TransformInto)); - } - } - } - name = faceName; - } - - // mtgjon related cards - if (card.contains("relatedCards")) { - QVariantMap givenRelated = card.value("relatedCards").toMap(); - // conjured cards from a spellbook - if (givenRelated.contains("spellbook")) { - auto spbk = givenRelated.value("spellbook").toStringList(); - for (const QString &spbkName : spbk) { - relatedCards.append( - new CardRelation(spbkName, CardRelationType::DoesNotAttach, false, false, 1, true)); - } - } - } - - CardInfoPtr newCard = addCard(name + numComponent, text, isToken, properties, relatedCards, printingInfo); - numCards++; + if (!set->contains(card)) { + card->addToSet(set); + cards++; } } - - // split cards handling - static const QString splitCardPropSeparator = QString(" // "); - static const QString splitCardTextSeparator = QString("\n\n---\n\n"); - static const QList noRelatedCards = {}; - - QList, QString>> partsAndNames = splitCards.values(); - for (auto [splitCardParts, name] : partsAndNames) { - QString text; - QVariantHash properties; - PrintingInfo printingInfo; - - for (const SplitCardPart &tmp : splitCardParts) { - if (!text.isEmpty()) { - text.append(splitCardTextSeparator); - } - text.append(tmp.getText()); - - if (properties.isEmpty()) { - properties = tmp.getProperties(); - printingInfo = tmp.getPrintingInfo(); - } else { - const QVariantHash &tmpProps = tmp.getProperties(); - for (auto i = tmpProps.cbegin(), end = tmpProps.cend(); i != end; ++i) { - QString prop = i.key(); - QString originalPropertyValue = properties.value(prop).toString(); - QString thisCardPropertyValue = i.value().toString(); - if (!thisCardPropertyValue.isEmpty() && originalPropertyValue != thisCardPropertyValue) { - if (originalPropertyValue.isEmpty()) { // don't create //es if one field is empty - properties.insert(prop, thisCardPropertyValue); - } else if (prop == "colors") { // the card is both colors - properties.insert(prop, originalPropertyValue + thisCardPropertyValue); - } else if (prop == "maintype") { // don't create maintypes with //es in them - continue; - } else { - properties.insert(prop, - originalPropertyValue + splitCardPropSeparator + thisCardPropertyValue); - } - } - } - } + + // split cards handling - get all unique card muids + QList muids = splitCards.uniqueKeys(); + foreach(int muid, muids) + { + // get all cards for this specific muid + QList maps = splitCards.values(muid); + QStringList names; + // now, reorder the cards using the ordered list of names + QMap orderedMaps; + foreach(QVariantMap map, maps) + { + if(names.isEmpty()) + names = map.contains("names") ? map.value("names").toStringList() : QStringList(); + QString name = map.value("name").toString(); + int index = names.indexOf(name); + orderedMaps.insertMulti(index, map); } - CardInfoPtr newCard = addCard(name, text, isToken, properties, noRelatedCards, printingInfo); - numCards++; + + // clean variables + cardName = ""; + cardCost = ""; + cmc = ""; + cardType = ""; + cardPT = ""; + cardText = ""; + colors.clear(); + // this is currently an integer; can't accept 2 values + cardLoyalty = 0; + + // loop cards and merge their contents + QString prefix = QString(" // "); + QString prefix2 = QString("\n\n---\n\n"); + foreach(QVariantMap map, orderedMaps.values()) + { + if(map.contains("name")) + { + if(!cardName.isEmpty()) + cardName += prefix; + cardName += map.value("name").toString(); + } + if(map.contains("manaCost")) + { + if(!cardCost.isEmpty()) + cardCost += prefix; + cardCost += map.value("manaCost").toString(); + } + if(map.contains("cmc")) + { + if(!cmc.isEmpty()) + cmc += prefix; + cmc += map.value("cmc").toString(); + } + if(map.contains("type")) + { + if(!cardType.isEmpty()) + cardType += prefix; + cardType += map.value("type").toString(); + } + if(map.contains("power") || map.contains("toughness")) + { + if(!cardPT.isEmpty()) + cardPT += prefix; + cardPT += map.value("power").toString() + QString('/') + map.value("toughness").toString(); + } + if(map.contains("text")) + { + if(!cardText.isEmpty()) + cardText += prefix2; + cardText += map.value("text").toString(); + } + + extractColors(map.value("colors").toStringList(), colors); + } + + colors.removeDuplicates(); + relatedCards = QStringList(); + reverseRelatedCards = QStringList(); + upsideDown = false; + + // add the card + CardInfo *card = addCard(set->getShortName(), cardName, false, muid, cardCost, cmc, cardType, cardPT, cardLoyalty, cardText, colors, relatedCards, reverseRelatedCards, upsideDown); + + if (!set->contains(card)) { + card->addToSet(set); + cards++; + } + } - return numCards; -} - -FormatRulesNameMap OracleImporter::createDefaultMagicFormats() -{ - // Predefined common exceptions - CardCondition superTypeIsBasic; - superTypeIsBasic.field = "type"; - superTypeIsBasic.matchType = "contains"; - superTypeIsBasic.value = "Basic Land"; - - ExceptionRule basicLands; - basicLands.conditions.append(superTypeIsBasic); - - CardCondition anyNumberAllowed; - anyNumberAllowed.field = "text"; - anyNumberAllowed.matchType = "contains"; - anyNumberAllowed.value = "A deck can have any number of"; - - ExceptionRule mayContainAnyNumber; - mayContainAnyNumber.conditions.append(anyNumberAllowed); - - // Map to store default rules - FormatRulesNameMap defaultFormatRulesNameMap; - - // ----------------- Helper lambda to create format ----------------- - auto makeFormat = [&](const QString &name, int minDeck = 60, int maxDeck = -1, int maxSideboardSize = 15, - const QList &allowedCounts = kConstructedCounts) -> FormatRulesPtr { - FormatRulesPtr f(new FormatRules); - f->formatName = name; - f->allowedCounts = allowedCounts; - f->minDeckSize = minDeck; - f->maxDeckSize = maxDeck; - f->maxSideboardSize = maxSideboardSize; - f->exceptions.append(basicLands); - f->exceptions.append(mayContainAnyNumber); - defaultFormatRulesNameMap.insert(name.toLower(), f); - return f; - }; - - // ----------------- Standard formats ----------------- - makeFormat("Standard"); - makeFormat("Modern"); - makeFormat("Legacy"); - makeFormat("Pioneer"); - makeFormat("Historic"); - makeFormat("Timeless"); - makeFormat("Future"); - makeFormat("OldSchool"); - makeFormat("Premodern"); - makeFormat("Pauper"); - makeFormat("Penny"); - - // ----------------- Singleton formats ----------------- - makeFormat("Commander", 100, 100, 15, kSingletonCounts); - makeFormat("Duel", 100, 100, 15, kSingletonCounts); - makeFormat("Brawl", 60, 60, 15, kSingletonCounts); - makeFormat("StandardBrawl", 60, 60, 15, kSingletonCounts); - makeFormat("Oathbreaker", 60, 60, 15, kSingletonCounts); - makeFormat("PauperCommander", 100, 100, 15, kSingletonCounts); - makeFormat("Predh", 100, 100, 15, kSingletonCounts); - - // ----------------- Restricted formats ----------------- - makeFormat("Vintage", 60, -1, 15, {{4, "legal"}, {1, "restricted"}, {0, "banned"}}); - - return defaultFormatRulesNameMap; + return cards; } int OracleImporter::startImport() { - static ICardSetPriorityController *noOpController = new NoopCardSetPriorityController(); + clear(); + + int setCards = 0, setIndex= 0; + QListIterator it(allSets); + const SetToDownload * curSet; // add an empty set for tokens - CardSetPtr tokenSet = - CardSet::newInstance(noOpController, CardSet::TOKENS_SETNAME, tr("Dummy set containing tokens"), "Tokens"); - sets.insert(CardSet::TOKENS_SETNAME, tokenSet); + CardSet *tokenSet = new CardSet(TOKENS_SETNAME, tr("Dummy set containing tokens"), "Tokens"); + sets.insert(TOKENS_SETNAME, tokenSet); - int setIndex = 0; + while (it.hasNext()) + { + curSet = & it.next(); + CardSet *set = new CardSet(curSet->getShortName(), curSet->getLongName(), curSet->getSetType(), curSet->getReleaseDate()); + if (!sets.contains(set->getShortName())) + sets.insert(set->getShortName(), set); - for (const SetToDownload &curSetToParse : allSets) { - CardSetPtr newSet = CardSet::newInstance(noOpController, curSetToParse.getShortName(), - curSetToParse.getLongName(), curSetToParse.getSetType(), - curSetToParse.getReleaseDate(), curSetToParse.getPriority()); - if (!sets.contains(newSet->getShortName())) - sets.insert(newSet->getShortName(), newSet); - - int numCardsInSet = importCardsFromSet(newSet, curSetToParse.getCards()); + int setCards = importTextSpoiler(set, curSet->getCards()); ++setIndex; - - emit setIndexChanged(numCardsInSet, setIndex, curSetToParse.getLongName()); + + emit setIndexChanged(setCards, setIndex, curSet->getLongName()); } - - emit setIndexChanged(0, setIndex, QString()); + + emit setIndexChanged(setCards, setIndex, QString()); // total number of sets return setIndex; } - -bool OracleImporter::saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion) -{ - CockatriceXml4Parser parser(new NoopCardPreferenceProvider(), new NoopCardSetPriorityController()); - - return parser.saveToFile(createDefaultMagicFormats(), sets, cards, fileName, sourceUrl, sourceVersion); -} - -void OracleImporter::clear() -{ - sets.clear(); - cards.clear(); - allSets.clear(); -} diff --git a/oracle/src/oracleimporter.h b/oracle/src/oracleimporter.h index 5bf352594..6a30e65bb 100644 --- a/oracle/src/oracleimporter.h +++ b/oracle/src/oracleimporter.h @@ -2,169 +2,47 @@ #define ORACLEIMPORTER_H #include -#include #include -#include -#include -// many users prefer not to see these sets with non english arts -// they will given priority PriorityLowest -const QStringList nonEnglishSets = {"4BB", "FBB", "PS11", "PSAL", "REN", "RIN"}; -const QMap setTypePriorities{ - {"core", CardSet::PriorityPrimary}, - {"expansion", CardSet::PriorityPrimary}, +#include - {"commander", CardSet::PrioritySecondary}, - {"starter", CardSet::PrioritySecondary}, - {"draft_innovation", CardSet::PrioritySecondary}, - {"duel_deck", CardSet::PrioritySecondary}, - - {"archenemy", CardSet::PriorityReprint}, - {"arsenal", CardSet::PriorityReprint}, - {"box", CardSet::PriorityReprint}, - {"eternal", CardSet::PriorityReprint}, - {"from_the_vault", CardSet::PriorityReprint}, - {"masterpiece", CardSet::PriorityReprint}, - {"masters", CardSet::PriorityReprint}, - {"memorabilia", CardSet::PriorityReprint}, - {"planechase", CardSet::PriorityReprint}, - {"premium_deck", CardSet::PriorityReprint}, - {"promo", CardSet::PriorityReprint}, - {"spellbook", CardSet::PriorityReprint}, - {"token", CardSet::PriorityReprint}, - {"treasure_chest", CardSet::PriorityReprint}, - - {"alchemy", CardSet::PriorityOther}, - {"funny", CardSet::PriorityOther}, - {"minigame", CardSet::PriorityOther}, - {"vanguard", CardSet::PriorityOther}, -}; - -class SetToDownload -{ +class SetToDownload { private: QString shortName, longName; - QList cards; + QVariant cards; QDate releaseDate; QString setType; - CardSet::Priority priority; - public: - const QString &getShortName() const - { - return shortName; - } - const QString &getLongName() const - { - return longName; - } - const QList &getCards() const - { - return cards; - } - const QString &getSetType() const - { - return setType; - } - const QDate &getReleaseDate() const - { - return releaseDate; - } - CardSet::Priority getPriority() const - { - return priority; - } - SetToDownload(QString _shortName, - QString _longName, - QList _cards, - CardSet::Priority _priority, - QString _setType = QString(), - const QDate &_releaseDate = QDate()) - : shortName(std::move(_shortName)), longName(std::move(_longName)), cards(std::move(_cards)), - releaseDate(_releaseDate), setType(std::move(_setType)), priority(_priority) - { - } - bool operator<(const SetToDownload &set) const - { - return longName.compare(set.longName, Qt::CaseInsensitive) < 0; - } + const QString &getShortName() const { return shortName; } + const QString &getLongName() const { return longName; } + const QVariant &getCards() const { return cards; } + const QString &getSetType() const { return setType; } + const QDate &getReleaseDate() const { return releaseDate; } + SetToDownload(const QString &_shortName, const QString &_longName, const QVariant &_cards, const QString &_setType = QString(), const QDate &_releaseDate = QDate()) + : shortName(_shortName), longName(_longName), cards(_cards), releaseDate(_releaseDate), setType(_setType) { } + bool operator<(const SetToDownload &set) const { return longName.compare(set.longName, Qt::CaseInsensitive) < 0; } }; -class SplitCardPart -{ -public: - SplitCardPart(const QString &_name, - const QString &_text, - const QVariantHash &_properties, - const PrintingInfo &_printingInfo); - inline const QString &getName() const - { - return name; - } - inline const QString &getText() const - { - return text; - } - inline const QVariantHash &getProperties() const - { - return properties; - } - inline const PrintingInfo &getPrintingInfo() const - { - return printingInfo; - } - -private: - QString name; - QString text; - QVariantHash properties; - PrintingInfo printingInfo; -}; - -class OracleImporter : public QObject -{ +class OracleImporter : public CardDatabase { Q_OBJECT private: - static const QRegularExpression formatRegex; - - /** - * The cards, indexed by name. - */ - CardNameMap cards; - - /** - * The sets, indexed by short name. - */ - SetNameMap sets; - QList allSets; - - CardInfoPtr addCard(QString name, - const QString &text, - bool isToken, - QVariantHash properties, - const QList &relatedCards, - const PrintingInfo &printingInfo); + QVariantMap setsMap; + QString dataDir; + + CardInfo *addCard(const QString &setName, QString cardName, bool isToken, int cardId, QString &cardCost, QString &cmc, const QString &cardType, const QString &cardPT, int cardLoyalty, const QString &cardText, const QStringList & colors, const QStringList & relatedCards, const QStringList & reverseRelatedCards, bool upsideDown); signals: void setIndexChanged(int cardsImported, int setIndex, const QString &setName); void dataReadProgress(int bytesRead, int totalBytes); - public: - explicit OracleImporter(QObject *parent = nullptr); + OracleImporter(const QString &_dataDir, QObject *parent = 0); bool readSetsFromByteArray(const QByteArray &data); int startImport(); - bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion); - int importCardsFromSet(const CardSetPtr ¤tSet, const QList &cardsList); - FormatRulesNameMap createDefaultMagicFormats(); - const CardNameMap &getCardList() const - { - return cards; - } - QList &getSets() - { - return allSets; - } - void clear(); + int importTextSpoiler(CardSet *set, const QVariant &data); + QList &getSets() { return allSets; } + const QString &getDataDir() const { return dataDir; } +protected: + void extractColors(const QStringList & in, QStringList & out); }; #endif diff --git a/oracle/src/oraclewizard.cpp b/oracle/src/oraclewizard.cpp index 2edb9e561..1c6e727d7 100644 --- a/oracle/src/oraclewizard.cpp +++ b/oracle/src/oraclewizard.cpp @@ -1,86 +1,76 @@ -#include "oraclewizard.h" - -#include "client/settings/cache_settings.h" -#include "main.h" -#include "oracleimporter.h" -#include "pages.h" -#include "pagetemplates.h" - +#include +#if QT_VERSION < 0x050000 + #include +#else + #include + #include +#endif +#include +#include #include +#include #include #include +#include +#include #include +#include +#include #include +#include #include +#include +#include #include -#include -#include +#include -OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent) -{ - // define a dummy context that will be used where needed - QString dummy = QT_TRANSLATE_NOOP("i18n", "English"); +#include "oraclewizard.h" +#include "oracleimporter.h" +#include "main.h" +#include "settingscache.h" -#ifdef Q_OS_WIN - setWizardStyle(QWizard::ModernStyle); +#define ZIP_SIGNATURE "PK" +#define ALLSETS_URL_FALLBACK "https://mtgjson.com/json/AllSets.json" + +#ifdef HAS_ZLIB + #include "zip/unzip.h" + #define ALLSETS_URL "https://mtgjson.com/json/AllSets.json.zip" +#else + #define ALLSETS_URL "https://mtgjson.com/json/AllSets.json" #endif - QString oracleSettingsFile = SettingsCache::instance().getSettingsPath() + "oracle.ini"; - settings = new QSettings(oracleSettingsFile, QSettings::IniFormat, this); +#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" - // We moved the oracle-specific settings from global.ini to a separate oracle.ini after 2.10 - if (!QFile::exists(oracleSettingsFile)) { - migrateOracleSettings(); - } - connect(&SettingsCache::instance(), &SettingsCache::langChanged, this, &OracleWizard::updateLanguage); +OracleWizard::OracleWizard(QWidget *parent) + : QWizard(parent) +{ + settings = new QSettings(settingsCache->getSettingsPath()+"global.ini",QSettings::IniFormat, this); + connect(settingsCache, SIGNAL(langChanged()), this, SLOT(updateLanguage())); - importer = new OracleImporter(this); + QString dataDir; - nam = new QNetworkAccessManager(this); +#ifndef PORTABLE_BUILD + #if QT_VERSION < 0x050000 + QDesktopServices::storageLocation(QDesktopServices::DataLocation); + #else + QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); + #endif +#else + dataDir.append("data"); +#endif - QList pages; + importer = new OracleImporter(dataDir, this); - if (!isSpoilersOnly) { - pages << new IntroPage << new LoadSetsPage << new SaveSetsPage << new LoadTokensPage << new OutroPage; - } else { - pages << new LoadSpoilersPage << new OutroPage; - } - - for (OracleWizardPage *page : pages) { - addPage(page); - - // Connect background auto-advance - connect(page, &OracleWizardPage::readyToContinue, this, [this]() { - if (backgroundMode) { - next(); - } - }); - } + addPage(new IntroPage); + addPage(new LoadSetsPage); + addPage(new SaveSetsPage); + addPage(new LoadTokensPage); + addPage(new SaveTokensPage); retranslateUi(); } -/** - * Migrates the oracle-specific settings from global.ini to oracle.ini - */ -void OracleWizard::migrateOracleSettings() -{ - QString filePath = SettingsCache::instance().getSettingsPath() + "global.ini"; - auto globalSettings = QSettings(filePath, QSettings::IniFormat, this); - - auto tryMigrateValue = [this, &globalSettings](const QString &name) { - QVariant variant = globalSettings.value(name); - if (variant.isValid()) { - settings->setValue(name, variant.toString()); - } - }; - - tryMigrateValue("allsetsurl"); - tryMigrateValue("tokensurl"); - tryMigrateValue("spoilersurl"); -} - void OracleWizard::updateLanguage() { qApp->removeTranslator(translator); @@ -89,19 +79,18 @@ void OracleWizard::updateLanguage() void OracleWizard::changeEvent(QEvent *event) { - if (event->type() == QEvent::LanguageChange) { + if (event->type() == QEvent::LanguageChange) retranslateUi(); - } - QDialog::changeEvent(event); } void OracleWizard::retranslateUi() { setWindowTitle(tr("Oracle Importer")); - for (int i = 0; i < pageIds().count(); i++) { + QWizard::setButtonText(QWizard::FinishButton, tr("Save")); + + for (int i = 0; i < pageIds().count(); i++) dynamic_cast(page(i))->retranslateUi(); - } } void OracleWizard::accept() @@ -121,19 +110,704 @@ void OracleWizard::disableButtons() button(QWizard::BackButton)->setDisabled(true); } -bool OracleWizard::saveTokensToFile(const QString &fileName) +bool OracleWizard::saveTokensToFile(const QString & fileName) { QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { + if(!file.open(QIODevice::WriteOnly)) + { qDebug() << "File open (w) failed for" << fileName; return false; } - - if (file.write(tokensData) == -1) { + if(file.write(tokensData) == -1) + { qDebug() << "File write (w) failed for" << fileName; return false; } - file.close(); return true; } + +IntroPage::IntroPage(QWidget *parent) + : OracleWizardPage(parent) +{ + label = new QLabel(this); + label->setWordWrap(true); + + languageLabel = new QLabel(this); + languageBox = new QComboBox(this); + QString setLanguage = settingsCache->getLang(); + QStringList qmFiles = findQmFiles(); + for (int i = 0; i < qmFiles.size(); i++) { + QString langName = languageName(qmFiles[i]); + languageBox->addItem(langName, qmFiles[i]); + if ((qmFiles[i] == setLanguage) || (setLanguage.isEmpty() && langName == tr("English"))) + languageBox->setCurrentIndex(i); + } + connect(languageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(languageBoxChanged(int))); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(label, 0, 0, 1, 2); + layout->addWidget(languageLabel, 1, 0); + layout->addWidget(languageBox, 1, 1); + + setLayout(layout); +} + +QStringList IntroPage::findQmFiles() +{ + QDir dir(translationPath); + QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); + fileNames.replaceInStrings(QRegExp(translationPrefix + "_(.*)\\.qm"), "\\1"); + return fileNames; +} + +QString IntroPage::languageName(const QString &qmFile) +{ + QTranslator translator; + translator.load(translationPrefix + "_" + qmFile + ".qm", translationPath); + + return translator.translate("IntroPage", "English"); +} + +void IntroPage::languageBoxChanged(int index) +{ + settingsCache->setLang(languageBox->itemData(index).toString()); +} + +void IntroPage::retranslateUi() +{ + setTitle(tr("Introduction")); + label->setText(tr("This wizard will import the list of sets, cards, and tokens " + "that will be used by Cockatrice." + "\nYou will need to specify a URL or a filename that " + "will be used as a source.")); + languageLabel->setText(tr("Language:")); +} + +LoadSetsPage::LoadSetsPage(QWidget *parent) + : OracleWizardPage(parent), nam(0) +{ + urlRadioButton = new QRadioButton(this); + fileRadioButton = new QRadioButton(this); + + urlLineEdit = new QLineEdit(this); + fileLineEdit = new QLineEdit(this); + + progressLabel = new QLabel(this); + progressBar = new QProgressBar(this); + + urlRadioButton->setChecked(true); + + urlButton = new QPushButton(this); + connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); + + fileButton = new QPushButton(this); + connect(fileButton, SIGNAL(clicked()), this, SLOT(actLoadSetsFile())); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(urlRadioButton, 0, 0); + layout->addWidget(urlLineEdit, 0, 1); + layout->addWidget(urlButton, 1, 1, Qt::AlignRight); + layout->addWidget(fileRadioButton, 2, 0); + layout->addWidget(fileLineEdit, 2, 1); + layout->addWidget(fileButton, 3, 1, Qt::AlignRight); + layout->addWidget(progressLabel, 4, 0); + layout->addWidget(progressBar, 4, 1); + + connect(&watcher, SIGNAL(finished()), this, SLOT(importFinished())); + + setLayout(layout); +} + +void LoadSetsPage::initializePage() +{ + urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); + + progressLabel->hide(); + progressBar->hide(); +} + +void LoadSetsPage::retranslateUi() +{ + setTitle(tr("Source selection")); + setSubTitle(tr("Please specify a source for the list of sets and cards. " + "You can specify a URL address that will be downloaded or " + "use an existing file from your computer.")); + + urlRadioButton->setText(tr("Download URL:")); + fileRadioButton->setText(tr("Local file:")); + urlButton->setText(tr("Restore default URL")); + fileButton->setText(tr("Choose file...")); +} + +void LoadSetsPage::actRestoreDefaultUrl() +{ + urlLineEdit->setText(ALLSETS_URL); +} + +void LoadSetsPage::actLoadSetsFile() +{ + QFileDialog dialog(this, tr("Load sets file")); + dialog.setFileMode(QFileDialog::ExistingFile); + +#ifdef HAS_ZLIB + dialog.setNameFilter(tr("Sets JSON file (*.json *.zip)")); +#else + dialog.setNameFilter(tr("Sets JSON file (*.json)")); +#endif + + if(!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) + dialog.selectFile(fileLineEdit->text()); + + if (!dialog.exec()) + return; + + fileLineEdit->setText(dialog.selectedFiles().at(0)); +} + +bool LoadSetsPage::validatePage() +{ + // once the import is finished, we call next(); skip validation + if(wizard()->importer->getSets().count() > 0) + return true; + + // else, try to import sets + if(urlRadioButton->isChecked()) + { + QUrl url = QUrl::fromUserInput(urlLineEdit->text()); + if(!url.isValid()) + { + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); + return false; + } + + progressLabel->setText(tr("Downloading (0MB)")); + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->show(); + progressBar->show(); + + wizard()->disableButtons(); + setEnabled(false); + + downloadSetsFile(url); + } else if(fileRadioButton->isChecked()) { + QFile setsFile(fileLineEdit->text()); + if(!setsFile.exists()) + { + QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); + return false; + } + + if (!setsFile.open(QIODevice::ReadOnly)) { + QMessageBox::critical(0, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); + return false; + } + + wizard()->disableButtons(); + setEnabled(false); + + readSetsFromByteArray(setsFile.readAll()); + + } + return false; +} + +void LoadSetsPage::downloadSetsFile(QUrl url) +{ + if(!nam) + nam = new QNetworkAccessManager(this); + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + + connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSetsFile())); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressSetsFile(qint64, qint64))); +} + +void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) +{ + if(total > 0) + { + progressBar->setMaximum(total); + progressBar->setValue(received); + } + progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); +} + +void LoadSetsPage::actDownloadFinishedSetsFile() +{ + // check for a reply + QNetworkReply *reply = static_cast(sender()); + QNetworkReply::NetworkError errorCode = reply->error(); + if (errorCode != QNetworkReply::NoError) { + QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); + + wizard()->enableButtons(); + setEnabled(true); + + reply->deleteLater(); + return; + } + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + qDebug() << "following redirect url:" << redirectUrl.toString(); + downloadSetsFile(redirectUrl); + reply->deleteLater(); + return; + } + + progressLabel->hide(); + progressBar->hide(); + + // save allsets.json url, but only if the user customized it and download was successfull + if(urlLineEdit->text() != QString(ALLSETS_URL)) + wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); + else + wizard()->settings->remove("allsetsurl"); + + readSetsFromByteArray(reply->readAll()); + reply->deleteLater(); +} + +void LoadSetsPage::readSetsFromByteArray(QByteArray data) +{ + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->setText(tr("Parsing file")); + progressLabel->show(); + progressBar->show(); + + // unzip the file if needed + if(data.startsWith(ZIP_SIGNATURE)) + { +#ifdef HAS_ZLIB + // zipped file + QBuffer *inBuffer = new QBuffer(&data); + QBuffer *outBuffer = new QBuffer(this); + QString fileName; + UnZip::ErrorCode ec; + UnZip uz; + + ec = uz.openArchive(inBuffer); + if (ec != UnZip::Ok) { + zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec))); + return; + } + + if(uz.fileList().size() != 1) + { + zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file.")); + return; + } + fileName = uz.fileList().at(0); + + outBuffer->open(QBuffer::ReadWrite); + ec = uz.extractFile(fileName, outBuffer); + if (ec != UnZip::Ok) { + zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec))); + uz.closeArchive(); + return; + } + + future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, outBuffer->data()); + watcher.setFuture(future); + return; +#else + zipDownloadFailed(tr("Sorry, this version of Oracle does not support zipped files.")); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + return; +#endif + } + // Start the computation. + future = QtConcurrent::run(wizard()->importer, &OracleImporter::readSetsFromByteArray, data); + watcher.setFuture(future); +} + +void LoadSetsPage::zipDownloadFailed(const QString &message) +{ + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + QMessageBox::StandardButton reply; + reply = QMessageBox::question(this, tr("Error"), message + "
" + tr("Do you want to try to download a fresh copy of the uncompressed file instead?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); + if (reply == QMessageBox::Yes) + { + urlRadioButton->setChecked(true); + urlLineEdit->setText(ALLSETS_URL_FALLBACK); + + wizard()->next(); + } +} + +void LoadSetsPage::importFinished() +{ + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + if(watcher.future().result()) + { + wizard()->next(); + } else { + QMessageBox::critical(this, tr("Error"), tr("The file was retrieved successfully, but it does not contain any sets data.")); + } +} + +SaveSetsPage::SaveSetsPage(QWidget *parent) + : OracleWizardPage(parent) +{ + defaultPathCheckBox = new QCheckBox(this); + defaultPathCheckBox->setChecked(true); + + messageLog = new QTextEdit(this); + messageLog->setReadOnly(true); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(defaultPathCheckBox, 0, 0); + layout->addWidget(messageLog, 1, 0); + + setLayout(layout); +} + +void SaveSetsPage::cleanupPage() +{ + disconnect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), 0, 0); +} + +void SaveSetsPage::initializePage() +{ + messageLog->clear(); + + connect(wizard()->importer, SIGNAL(setIndexChanged(int, int, const QString &)), this, SLOT(updateTotalProgress(int, int, const QString &))); + + if (!wizard()->importer->startImport()) + QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); +} + +void SaveSetsPage::retranslateUi() +{ + setTitle(tr("Sets imported")); + setSubTitle(tr("The following sets has been imported. " + "Press \"Save\" to save the imported cards to the Cockatrice database.")); + + defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); + #ifdef PORTABLE_BUILD + defaultPathCheckBox->setEnabled(false); + #endif +} + +void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) +{ + if (setName.isEmpty()) { + messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + ""); + } else { + messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported)); + } + messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum()); +} + +bool SaveSetsPage::validatePage() +{ + bool ok = false; + QString dataDir; + #ifndef PORTABLE_BUILD +#if QT_VERSION < 0x050000 + dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#else + dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); +#endif +#else + dataDir = qApp->applicationDirPath() + "/data"; +#endif + +#ifdef PORTABLE_BUILD + QSettings* settings = new QSettings("settings/global.ini",QSettings::IniFormat,this); + QString defaultPath = "data/cards.xml"; + settings->setValue("paths/carddatabase", defaultPath); +#else + QSettings* settings = new QSettings(settingsCache->getSettingsPath()+"global.ini",QSettings::IniFormat,this); + QString defaultPath = settings->value("paths/carddatabase").toString(); +#endif + QString windowName = tr("Save card database"); + QString fileType = tr("XML; card database (*.xml)"); + + do { + QString fileName; + if (defaultPath.isEmpty()) { + if (defaultPathCheckBox->isChecked()) + fileName = dataDir + "/cards.xml"; + else + fileName = QFileDialog::getSaveFileName(this, windowName, dataDir + "/cards.xml", fileType); + + settings->setValue("paths/carddatabase", fileName); + } + else { + if (defaultPathCheckBox->isChecked()) + fileName = defaultPath; + else + fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } + if (fileName.isEmpty()) { + return false; + } + + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + return false; + } + if (wizard()->importer->saveToFile(fileName)) + { + ok = true; + QMessageBox::information(this, + tr("Success"), + tr("The card database has been saved successfully to\n%1").arg(fileName)); + } else { + QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; + if (defaultPathCheckBox->isChecked()) + defaultPathCheckBox->setChecked(false); + } + } while (!ok); + + return true; +} + +LoadTokensPage::LoadTokensPage(QWidget *parent) + : OracleWizardPage(parent), nam(0) +{ + urlLabel = new QLabel(this); + urlLineEdit = new QLineEdit(this); + + progressLabel = new QLabel(this); + progressBar = new QProgressBar(this); + + urlButton = new QPushButton(this); + connect(urlButton, SIGNAL(clicked()), this, SLOT(actRestoreDefaultUrl())); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(urlLabel, 0, 0); + layout->addWidget(urlLineEdit, 0, 1); + layout->addWidget(urlButton, 1, 1, Qt::AlignRight); + layout->addWidget(progressLabel, 2, 0); + layout->addWidget(progressBar, 2, 1); + + setLayout(layout); +} + +void LoadTokensPage::initializePage() +{ + urlLineEdit->setText(wizard()->settings->value("tokensurl", TOKENS_URL).toString()); + + progressLabel->hide(); + progressBar->hide(); +} + +void LoadTokensPage::retranslateUi() +{ + setTitle(tr("Tokens source selection")); + setSubTitle(tr("Please specify a source for the list of tokens. " + "You can specify a URL address that will be downloaded or " + "use an existing file from your computer.")); + + urlLabel->setText(tr("Download URL:")); + urlButton->setText(tr("Restore default URL")); +} + +void LoadTokensPage::actRestoreDefaultUrl() +{ + urlLineEdit->setText(TOKENS_URL); +} + +bool LoadTokensPage::validatePage() +{ + // once the import is finished, we call next(); skip validation + if(wizard()->hasTokensData()) + return true; + + QUrl url = QUrl::fromUserInput(urlLineEdit->text()); + if(!url.isValid()) + { + QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); + return false; + } + + progressLabel->setText(tr("Downloading (0MB)")); + // show an infinite progressbar + progressBar->setMaximum(0); + progressBar->setMinimum(0); + progressBar->setValue(0); + progressLabel->show(); + progressBar->show(); + + wizard()->disableButtons(); + setEnabled(false); + + downloadTokensFile(url); + return false; +} + +void LoadTokensPage::downloadTokensFile(QUrl url) +{ + if(!nam) + nam = new QNetworkAccessManager(this); + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + + connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedTokensFile())); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(actDownloadProgressTokensFile(qint64, qint64))); +} + +void LoadTokensPage::actDownloadProgressTokensFile(qint64 received, qint64 total) +{ + if(total > 0) + { + progressBar->setMaximum(total); + progressBar->setValue(received); + } + progressLabel->setText(tr("Downloading (%1MB)").arg((int) received / (1024 * 1024))); +} + +void LoadTokensPage::actDownloadFinishedTokensFile() +{ + // check for a reply + QNetworkReply *reply = static_cast(sender()); + QNetworkReply::NetworkError errorCode = reply->error(); + if (errorCode != QNetworkReply::NoError) { + QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); + + wizard()->enableButtons(); + setEnabled(true); + + reply->deleteLater(); + return; + } + + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (statusCode == 301 || statusCode == 302) { + QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + qDebug() << "following redirect url:" << redirectUrl.toString(); + downloadTokensFile(redirectUrl); + reply->deleteLater(); + return; + } + + progressLabel->hide(); + progressBar->hide(); + + // save tokens.xml url, but only if the user customized it and download was successfull + if(urlLineEdit->text() != QString(TOKENS_URL)) + wizard()->settings->setValue("tokensurl", urlLineEdit->text()); + else + wizard()->settings->remove("tokensurl"); + + wizard()->setTokensData(reply->readAll()); + reply->deleteLater(); + + wizard()->enableButtons(); + setEnabled(true); + progressLabel->hide(); + progressBar->hide(); + + wizard()->next(); +} + +SaveTokensPage::SaveTokensPage(QWidget *parent) + : OracleWizardPage(parent) +{ + defaultPathCheckBox = new QCheckBox(this); + defaultPathCheckBox->setChecked(true); + + QGridLayout *layout = new QGridLayout(this); + layout->addWidget(defaultPathCheckBox, 0, 0); + + setLayout(layout); +} + +void SaveTokensPage::retranslateUi() +{ + setTitle(tr("Tokens imported")); + setSubTitle(tr("The tokens has been imported. " + "Press \"Save\" to save the imported tokens to the Cockatrice tokens database.")); + + defaultPathCheckBox->setText(tr("Save to the default path (recommended)")); + #ifdef PORTABLE_BUILD + defaultPathCheckBox->setEnabled(false); + #endif +} + +bool SaveTokensPage::validatePage() +{ + bool ok = false; + QString dataDir; + #ifndef PORTABLE_BUILD +#if QT_VERSION < 0x050000 + dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation); +#else + dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first(); +#endif +#else + dataDir = qApp->applicationDirPath() + "/data"; +#endif + +#ifdef PORTABLE_BUILD + QSettings* settings = new QSettings("settings/global.ini",QSettings::IniFormat,this); + QString defaultPath = "data/tokens.xml"; + settings->setValue("paths/tokendatabase", defaultPath); +#else + QSettings* settings = new QSettings(settingsCache->getSettingsPath()+"global.ini",QSettings::IniFormat,this); + QString defaultPath = settings->value("paths/tokendatabase").toString(); +#endif + + QString windowName = tr("Save token database"); + QString fileType = tr("XML; token database (*.xml)"); + + do { + QString fileName; + if (defaultPath.isEmpty()) { + if (defaultPathCheckBox->isChecked()) + fileName = dataDir + "/tokens.xml"; + else + fileName = QFileDialog::getSaveFileName(this, windowName, dataDir + "/tokens.xml", fileType); + settings->setValue("paths/tokendatabase", fileName); + } + else { + if (defaultPathCheckBox->isChecked()) + fileName = defaultPath; + else + fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); + } + if (fileName.isEmpty()) { + return false; + } + + QFileInfo fi(fileName); + QDir fileDir(fi.path()); + if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { + return false; + } + if (wizard()->saveTokensToFile(fileName)) + { + ok = true; + QMessageBox::information(this, + tr("Success"), + tr("The token database has been saved successfully to\n%1").arg(fileName)); + } else { + QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName));; + if (defaultPathCheckBox->isChecked()) + defaultPathCheckBox->setChecked(false); + } + } while (!ok); + + return true; +} diff --git a/oracle/src/oraclewizard.h b/oracle/src/oraclewizard.h index 78427175c..832144c35 100644 --- a/oracle/src/oraclewizard.h +++ b/oracle/src/oraclewizard.h @@ -2,7 +2,8 @@ #define ORACLEWIZARD_H #include -#include +#include +#include class QCheckBox; class QGroupBox; @@ -19,66 +20,139 @@ class QSettings; class OracleWizard : public QWizard { - Q_OBJECT + Q_OBJECT public: - explicit OracleWizard(QWidget *parent = nullptr); - void accept() override; - void enableButtons(); - void disableButtons(); - void retranslateUi(); - void setTokensData(QByteArray _tokensData) - { - tokensData = std::move(_tokensData); - } - bool hasTokensData() - { - return !tokensData.isEmpty(); - } - void setCardSourceUrl(const QString &sourceUrl) - { - cardSourceUrl = sourceUrl; - } - void setCardSourceVersion(const QString &sourceVersion) - { - cardSourceVersion = sourceVersion; - } - const QString &getCardSourceUrl() const - { - return cardSourceUrl; - } - const QString &getCardSourceVersion() const - { - return cardSourceVersion; - } - bool saveTokensToFile(const QString &fileName); - - void runInBackground() - { - backgroundMode = true; - hide(); - currentPage()->initializePage(); - } - -public: - OracleImporter *importer; - QSettings *settings; - QNetworkAccessManager *nam; - bool downloadedPlainXml = false; - QByteArray xmlData; - bool backgroundMode = false; - + OracleWizard(QWidget *parent = 0); + void accept(); + void enableButtons(); + void disableButtons(); + void retranslateUi(); + void setTokensData(QByteArray _tokensData) { tokensData = _tokensData; } + bool hasTokensData() { return !tokensData.isEmpty(); } + bool saveTokensToFile(const QString & fileName); +public: + OracleImporter *importer; + QSettings * settings; private slots: void updateLanguage(); - private: + QStringList findQmFiles(); + QString languageName(const QString &qmFile); QByteArray tokensData; - QString cardSourceUrl; - QString cardSourceVersion; - - void migrateOracleSettings(); - protected: - void changeEvent(QEvent *event) override; + void changeEvent(QEvent *event); }; -#endif + +class OracleWizardPage : public QWizardPage +{ + Q_OBJECT +public: + OracleWizardPage(QWidget *parent = 0): QWizardPage(parent) {}; + virtual void retranslateUi() = 0; +protected: + inline OracleWizard *wizard() { return (OracleWizard*) QWizardPage::wizard(); }; +}; + +class IntroPage : public OracleWizardPage +{ + Q_OBJECT +public: + IntroPage(QWidget *parent = 0); + void retranslateUi(); +private: + QStringList findQmFiles(); + QString languageName(const QString &qmFile); +private: + QLabel *label, *languageLabel; + QComboBox *languageBox; +private slots: + void languageBoxChanged(int index); +}; + +class LoadSetsPage : public OracleWizardPage +{ + Q_OBJECT +public: + LoadSetsPage(QWidget *parent = 0); + void retranslateUi(); +protected: + void initializePage(); + bool validatePage(); + void readSetsFromByteArray(QByteArray data); + void downloadSetsFile(QUrl url); +private: + QRadioButton *urlRadioButton; + QRadioButton *fileRadioButton; + QLineEdit *urlLineEdit; + QLineEdit *fileLineEdit; + QPushButton *urlButton; + QPushButton *fileButton; + QLabel *progressLabel; + QProgressBar * progressBar; + + QNetworkAccessManager *nam; + QFutureWatcher watcher; + QFuture future; +private slots: + void actLoadSetsFile(); + void actRestoreDefaultUrl(); + void actDownloadProgressSetsFile(qint64 received, qint64 total); + void actDownloadFinishedSetsFile(); + void importFinished(); + void zipDownloadFailed(const QString &message); +}; + +class SaveSetsPage : public OracleWizardPage +{ + Q_OBJECT +public: + SaveSetsPage(QWidget *parent = 0); + void retranslateUi(); +private: + QTextEdit *messageLog; + QCheckBox * defaultPathCheckBox; +protected: + void initializePage(); + void cleanupPage(); + bool validatePage(); +private slots: + void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); +}; + +class LoadTokensPage : public OracleWizardPage +{ + Q_OBJECT +public: + LoadTokensPage(QWidget *parent = 0); + void retranslateUi(); +protected: + void initializePage(); + bool validatePage(); + void downloadTokensFile(QUrl url); +private: + QLabel *urlLabel; + QLineEdit *urlLineEdit; + QPushButton *urlButton; + QLabel *progressLabel; + QProgressBar * progressBar; + + QNetworkAccessManager *nam; +private slots: + void actRestoreDefaultUrl(); + void actDownloadProgressTokensFile(qint64 received, qint64 total); + void actDownloadFinishedTokensFile(); +}; + +class SaveTokensPage : public OracleWizardPage +{ + Q_OBJECT +public: + SaveTokensPage(QWidget *parent = 0); + void retranslateUi(); +private: + QCheckBox * defaultPathCheckBox; +protected: + bool validatePage(); +}; +#endif \ No newline at end of file diff --git a/oracle/src/pages.cpp b/oracle/src/pages.cpp deleted file mode 100644 index 7629a291b..000000000 --- a/oracle/src/pages.cpp +++ /dev/null @@ -1,742 +0,0 @@ -#include "pages.h" - -#include "client/settings/cache_settings.h" -#include "main.h" -#include "oracleimporter.h" -#include "oraclewizard.h" -#include "pages.h" -#include "pagetemplates.h" -#include "version_string.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAS_LZMA -#include "lzma/decompress.h" -#endif - -#ifdef HAS_ZLIB -#include "zip/unzip.h" -#endif - -#define ZIP_SIGNATURE "PK" -// Xz stream header: 0xFD + "7zXZ" -#define XZ_SIGNATURE "\xFD\x37\x7A\x58\x5A" -#define MTGJSON_V4_URL_COMPONENT "mtgjson.com/files/" -#define ALLSETS_URL_FALLBACK "https://www.mtgjson.com/api/v5/AllPrintings.json" -#define MTGJSON_VERSION_URL "https://www.mtgjson.com/api/v5/Meta.json" - -#ifdef HAS_LZMA -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.xz" -#elif defined(HAS_ZLIB) -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json.zip" -#else -#define ALLSETS_URL "https://www.mtgjson.com/api/v5/AllPrintings.json" -#endif - -#define TOKENS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml" -#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml" - -IntroPage::IntroPage(QWidget *parent) : OracleWizardPage(parent) -{ - label = new QLabel(this); - label->setWordWrap(true); - - languageLabel = new QLabel(this); - versionLabel = new QLabel(this); - languageBox = new QComboBox(this); - - QStringList languageCodes = findQmFiles(); - for (const QString &code : languageCodes) { - QString langName = languageName(code); - languageBox->addItem(langName, code); - } - - QString setLanguage = QCoreApplication::translate("i18n", DEFAULT_LANG_NAME); - int index = languageBox->findText(setLanguage, Qt::MatchExactly); - if (index == -1) { - qWarning() << "could not find language" << setLanguage; - } else { - languageBox->setCurrentIndex(index); - } - - connect(languageBox, qOverload(&QComboBox::currentIndexChanged), this, &IntroPage::languageBoxChanged); - - auto *layout = new QGridLayout(this); - layout->addWidget(label, 0, 0, 1, 2); - layout->addWidget(languageLabel, 1, 0); - layout->addWidget(languageBox, 1, 1); - layout->addWidget(versionLabel, 2, 0, 1, 2); - - setLayout(layout); -} - -void IntroPage::initializePage() -{ - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -QStringList IntroPage::findQmFiles() -{ - QDir dir(translationPath); - QStringList fileNames = dir.entryList(QStringList(translationPrefix + "_*.qm"), QDir::Files, QDir::Name); - fileNames.replaceInStrings(QRegularExpression(translationPrefix + "_(.*)\\.qm"), "\\1"); - return fileNames; -} - -QString IntroPage::languageName(const QString &lang) -{ - QTranslator qTranslator; - - QString appNameHint = translationPrefix + "_" + lang; - bool appTranslationLoaded = qTranslator.load(appNameHint, translationPath); - if (!appTranslationLoaded) { - qDebug() << "Unable to load" << translationPrefix << "translation" << appNameHint << "at" << translationPath; - } - - return qTranslator.translate("i18n", DEFAULT_LANG_NAME); -} - -void IntroPage::languageBoxChanged(int index) -{ - SettingsCache::instance().setLang(languageBox->itemData(index).toString()); -} - -void IntroPage::retranslateUi() -{ - setTitle(tr("Introduction")); - label->setText(tr("This wizard will import the list of sets, cards, and tokens " - "that will be used by Cockatrice.")); - languageLabel->setText(tr("Interface language:")); - versionLabel->setText(tr("Version:") + QString(" %1").arg(VERSION_STRING)); -} - -void OutroPage::retranslateUi() -{ - setTitle(tr("Finished")); - setSubTitle(tr("The wizard has finished.") + "
" + - tr("You can now start using Cockatrice with the newly updated cards.") + "

" + - tr("If the card databases don't reload automatically, restart the Cockatrice client.")); -} - -void OutroPage::initializePage() -{ - if (wizard()->backgroundMode) { - wizard()->accept(); - exit(0); - } -} - -LoadSetsPage::LoadSetsPage(QWidget *parent) : OracleWizardPage(parent) -{ - urlRadioButton = new QRadioButton(this); - fileRadioButton = new QRadioButton(this); - - urlLineEdit = new QLineEdit(this); - fileLineEdit = new QLineEdit(this); - - progressLabel = new QLabel(this); - progressBar = new QProgressBar(this); - - urlRadioButton->setChecked(true); - - urlButton = new QPushButton(this); - connect(urlButton, &QPushButton::clicked, this, &LoadSetsPage::actRestoreDefaultUrl); - - fileButton = new QPushButton(this); - connect(fileButton, &QPushButton::clicked, this, &LoadSetsPage::actLoadSetsFile); - - auto *layout = new QGridLayout(this); - layout->addWidget(urlRadioButton, 0, 0); - layout->addWidget(urlLineEdit, 0, 1); - layout->addWidget(urlButton, 1, 1, Qt::AlignRight); - layout->addWidget(fileRadioButton, 2, 0); - layout->addWidget(fileLineEdit, 2, 1); - layout->addWidget(fileButton, 3, 1, Qt::AlignRight); - layout->addWidget(progressLabel, 4, 0); - layout->addWidget(progressBar, 4, 1); - - connect(&watcher, &QFutureWatcher::finished, this, &LoadSetsPage::importFinished); - - setLayout(layout); -} - -void LoadSetsPage::initializePage() -{ - urlLineEdit->setText(wizard()->settings->value("allsetsurl", ALLSETS_URL).toString()); - - progressLabel->hide(); - progressBar->hide(); - - if (wizard()->backgroundMode) { - if (isEnabled()) { - validatePage(); - } - } -} - -void LoadSetsPage::retranslateUi() -{ - setTitle(tr("Source selection")); - setSubTitle(tr("Please specify a compatible source for the list of sets and cards. " - "You can specify a URL address that will be downloaded or " - "use an existing file from your computer.")); - - urlRadioButton->setText(tr("Download URL:")); - fileRadioButton->setText(tr("Local file:")); - urlButton->setText(tr("Restore default URL")); - fileButton->setText(tr("Choose file...")); -} - -void LoadSetsPage::actRestoreDefaultUrl() -{ - urlLineEdit->setText(ALLSETS_URL); -} - -void LoadSetsPage::actLoadSetsFile() -{ - QFileDialog dialog(this, tr("Load sets file")); - dialog.setFileMode(QFileDialog::ExistingFile); - - QString extensions = "*.json *.xml"; -#ifdef HAS_ZLIB - extensions += " *.zip"; -#endif -#ifdef HAS_LZMA - extensions += " *.xz"; -#endif - dialog.setNameFilter(tr("Sets file (%1)").arg(extensions)); - - if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) { - dialog.selectFile(fileLineEdit->text()); - } - - if (!dialog.exec()) { - return; - } - - fileLineEdit->setText(dialog.selectedFiles().at(0)); -} - -bool LoadSetsPage::validatePage() -{ - // once the import is finished, we call next(); skip validation - if (wizard()->downloadedPlainXml || wizard()->importer->getSets().count() > 0) { - return true; - } - - // else, try to import sets - if (urlRadioButton->isChecked()) { - // If a user attempts to download from V4, redirect them to V5 - if (urlLineEdit->text().contains(MTGJSON_V4_URL_COMPONENT)) { - actRestoreDefaultUrl(); - } - - const auto url = QUrl::fromUserInput(urlLineEdit->text()); - - if (!url.isValid()) { - QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid.")); - return false; - } - - progressLabel->setText(tr("Downloading (0MB)")); - // show an infinite progressbar - progressBar->setMaximum(0); - progressBar->setMinimum(0); - progressBar->setValue(0); - progressLabel->show(); - progressBar->show(); - - wizard()->disableButtons(); - setEnabled(false); - - downloadSetsFile(url); - } else if (fileRadioButton->isChecked()) { - QFile setsFile(fileLineEdit->text()); - if (!setsFile.exists()) { - QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); - return false; - } - - if (!setsFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); - return false; - } - - wizard()->disableButtons(); - setEnabled(false); - - wizard()->setCardSourceUrl(setsFile.fileName()); - wizard()->setCardSourceVersion("unknown"); - - readSetsFromByteArray(setsFile.readAll()); - } - - return false; -} - -void LoadSetsPage::downloadSetsFile(const QUrl &url) -{ - wizard()->setCardSourceVersion("unknown"); - - const auto urlString = url.toString(); - if (urlString == ALLSETS_URL || urlString == ALLSETS_URL_FALLBACK) { - const auto versionUrl = QUrl::fromUserInput(MTGJSON_VERSION_URL); - QNetworkRequest request = QNetworkRequest(versionUrl); - request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING)); - auto *versionReply = wizard()->nam->get(request); - connect(versionReply, &QNetworkReply::finished, [this, versionReply]() { - if (versionReply->error() == QNetworkReply::NoError) { - auto data = versionReply->readAll(); - QJsonParseError jsonError{}; - auto jsonResponse = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error == QJsonParseError::NoError) { - const auto jsonMap = jsonResponse.toVariant().toMap(); - - auto versionString = jsonMap.value("meta").toMap().value("version").toString(); - if (versionString.isEmpty()) { - versionString = "unknown"; - } - wizard()->setCardSourceVersion(versionString); - } - } - - versionReply->deleteLater(); - }); - } - - wizard()->setCardSourceUrl(url.toString()); - - QNetworkRequest request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::UserAgentHeader, QString("Cockatrice %1").arg(VERSION_STRING)); - auto *reply = wizard()->nam->get(request); - - connect(reply, &QNetworkReply::finished, this, &LoadSetsPage::actDownloadFinishedSetsFile); - connect(reply, &QNetworkReply::downloadProgress, this, &LoadSetsPage::actDownloadProgressSetsFile); -} - -void LoadSetsPage::actDownloadProgressSetsFile(qint64 received, qint64 total) -{ - if (total > 0) { - progressBar->setMaximum(static_cast(total)); - progressBar->setValue(static_cast(received)); - } - progressLabel->setText(tr("Downloading (%1MB)").arg((int)received / (1024 * 1024))); -} - -void LoadSetsPage::actDownloadFinishedSetsFile() -{ - // check for a reply - auto *reply = dynamic_cast(sender()); - auto errorCode = reply->error(); - if (errorCode != QNetworkReply::NoError) { - QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); - - wizard()->enableButtons(); - setEnabled(true); - - reply->deleteLater(); - return; - } - - auto statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { - const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - qDebug() << "following redirect url:" << redirectUrl.toString(); - downloadSetsFile(redirectUrl); - reply->deleteLater(); - return; - } - - progressLabel->hide(); - progressBar->hide(); - - // save AllPrintings.json url, but only if the user customized it and download was successful - if (urlLineEdit->text() != QString(ALLSETS_URL)) { - wizard()->settings->setValue("allsetsurl", urlLineEdit->text()); - } else { - wizard()->settings->remove("allsetsurl"); - } - - readSetsFromByteArray(reply->readAll()); - reply->deleteLater(); -} - -void LoadSetsPage::readSetsFromByteArray(QByteArray _data) -{ - // show an infinite progressbar - progressBar->setMaximum(0); - progressBar->setMinimum(0); - progressBar->setValue(0); - progressLabel->setText(tr("Parsing file")); - progressLabel->show(); - progressBar->show(); - - wizard()->downloadedPlainXml = false; - wizard()->xmlData.clear(); - readSetsFromByteArrayRef(_data); -} - -void LoadSetsPage::readSetsFromByteArrayRef(QByteArray &_data) -{ - // unzip the file if needed - if (_data.startsWith(XZ_SIGNATURE)) { -#ifdef HAS_LZMA - // zipped file - auto *inBuffer = new QBuffer(&_data); - auto newData = QByteArray(); - auto *outBuffer = new QBuffer(&newData); - inBuffer->open(QBuffer::ReadOnly); - outBuffer->open(QBuffer::WriteOnly); - XzDecompressor xz; - if (!xz.decompress(inBuffer, outBuffer)) { - zipDownloadFailed(tr("Xz extraction failed.")); - return; - } - _data.clear(); - readSetsFromByteArrayRef(newData); - return; -#else - zipDownloadFailed(tr("Sorry, this version of Oracle does not support xz compressed files.")); - - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - return; -#endif - } else if (_data.startsWith(ZIP_SIGNATURE)) { -#ifdef HAS_ZLIB - // zipped file - auto *inBuffer = new QBuffer(&_data); - auto newData = QByteArray(); - auto *outBuffer = new QBuffer(&newData); - QString fileName; - UnZip::ErrorCode ec; - UnZip uz; - - ec = uz.openArchive(inBuffer); - if (ec != UnZip::Ok) { - zipDownloadFailed(tr("Failed to open Zip archive: %1.").arg(uz.formatError(ec))); - return; - } - - if (uz.fileList().size() != 1) { - zipDownloadFailed(tr("Zip extraction failed: the Zip archive doesn't contain exactly one file.")); - return; - } - fileName = uz.fileList().at(0); - - outBuffer->open(QBuffer::ReadWrite); - ec = uz.extractFile(fileName, outBuffer); - if (ec != UnZip::Ok) { - zipDownloadFailed(tr("Zip extraction failed: %1.").arg(uz.formatError(ec))); - uz.closeArchive(); - return; - } - _data.clear(); - readSetsFromByteArrayRef(newData); - return; -#else - zipDownloadFailed(tr("Sorry, this version of Oracle does not support zipped files.")); - - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - return; -#endif - } else if (_data.startsWith("{")) { - // Start the computation. - jsonData = std::move(_data); - future = QtConcurrent::run([this] { return wizard()->importer->readSetsFromByteArray(std::move(jsonData)); }); - watcher.setFuture(future); - } else if (_data.startsWith("<")) { - // save xml file and don't do any processing - wizard()->downloadedPlainXml = true; - wizard()->xmlData = std::move(_data); - importFinished(); - } else { - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - QMessageBox::critical(this, tr("Error"), tr("Failed to interpret downloaded data.")); - } -} - -void LoadSetsPage::zipDownloadFailed(const QString &message) -{ - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - - QMessageBox::StandardButton reply; - reply = static_cast(QMessageBox::question( - this, tr("Error"), message + "
" + tr("Do you want to download the uncompressed file instead?"), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)); - - if (reply == QMessageBox::Yes) { - urlRadioButton->setChecked(true); - urlLineEdit->setText(ALLSETS_URL_FALLBACK); - - wizard()->next(); - } -} - -void LoadSetsPage::importFinished() -{ - wizard()->enableButtons(); - setEnabled(true); - progressLabel->hide(); - progressBar->hide(); - - if (wizard()->downloadedPlainXml || watcher.future().result()) { - wizard()->next(); - } else { - QMessageBox::critical(this, tr("Error"), - tr("The file was retrieved successfully, but it does not contain any sets data.")); - } -} - -SaveSetsPage::SaveSetsPage(QWidget *parent) : OracleWizardPage(parent) -{ - pathLabel = new QLabel(this); - saveLabel = new QLabel(this); - - defaultPathCheckBox = new QCheckBox(this); - - messageLog = new QTextEdit(this); - messageLog->setReadOnly(true); - - auto *layout = new QGridLayout(this); - layout->addWidget(messageLog, 0, 0); - layout->addWidget(saveLabel, 1, 0); - layout->addWidget(pathLabel, 2, 0); - layout->addWidget(defaultPathCheckBox, 3, 0); - - setLayout(layout); -} - -void SaveSetsPage::cleanupPage() -{ - wizard()->importer->clear(); - disconnect(wizard()->importer, &OracleImporter::setIndexChanged, nullptr, nullptr); -} - -void SaveSetsPage::initializePage() -{ - messageLog->clear(); - - retranslateUi(); - if (wizard()->downloadedPlainXml) { - messageLog->hide(); - } else { - messageLog->show(); - connect(wizard()->importer, &OracleImporter::setIndexChanged, this, &SaveSetsPage::updateTotalProgress); - - int setsImported = wizard()->importer->startImport(); - - if (setsImported == 0) { - QMessageBox::critical(this, tr("Error"), tr("No set has been imported.")); - } - } - - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -void SaveSetsPage::retranslateUi() -{ - setTitle(tr("Sets imported")); - if (wizard()->downloadedPlainXml) { - setSubTitle(tr("A cockatrice database file of %1 MB has been downloaded.") - .arg(qRound(wizard()->xmlData.size() / 1000000.0))); - } else { - setSubTitle(tr("The following sets have been found:")); - } - - saveLabel->setText(tr("Press \"Save\" to store the imported cards in the Cockatrice database.")); - pathLabel->setText(tr("The card database will be saved at the following location:") + "
" + - SettingsCache::instance().getCardDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); - - setButtonText(QWizard::NextButton, tr("&Save")); -} - -void SaveSetsPage::updateTotalProgress(int cardsImported, int /* setIndex */, const QString &setName) -{ - if (setName.isEmpty()) { - messageLog->append("" + tr("Import finished: %1 cards.").arg(wizard()->importer->getCardList().size()) + - ""); - } else { - messageLog->append(tr("%1: %2 cards imported").arg(setName).arg(cardsImported)); - } - - messageLog->verticalScrollBar()->setValue(messageLog->verticalScrollBar()->maximum()); -} - -bool SaveSetsPage::validatePage() -{ - QString defaultPath = SettingsCache::instance().getCardDatabasePath(); - QString windowName = tr("Save card database"); - QString fileType = tr("XML; card database (*.xml)"); - - QString fileName; - if (defaultPathCheckBox->isChecked()) { - fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); - } else { - fileName = defaultPath; - } - - if (fileName.isEmpty()) { - return false; - } - - QFileInfo fi(fileName); - QDir fileDir(fi.path()); - if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { - return false; - } - - if (wizard()->downloadedPlainXml) { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - qDebug() << "File write (w) failed for" << fileName; - return false; - } - if (file.write(wizard()->xmlData) < 1) { - qDebug() << "File write (w) failed for" << fileName; - return false; - } - wizard()->xmlData.clear(); - } else if (!wizard()->importer->saveToFile(fileName, wizard()->getCardSourceUrl(), - wizard()->getCardSourceVersion())) { - QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName)); - return false; - } - - return true; -} - -void LoadTokensPage::initializePage() -{ - SimpleDownloadFilePage::initializePage(); - - if (wizard()->backgroundMode) { - emit readyToContinue(); - } -} - -QString LoadTokensPage::getDefaultUrl() -{ - return TOKENS_URL; -} - -QString LoadTokensPage::getCustomUrlSettingsKey() -{ - return "tokensurl"; -} - -QString LoadTokensPage::getDefaultSavePath() -{ - return SettingsCache::instance().getTokenDatabasePath(); -} - -QString LoadTokensPage::getWindowTitle() -{ - return tr("Save token database"); -} - -QString LoadTokensPage::getFileType() -{ - return tr("XML; token database (*.xml)"); -} - -QString LoadTokensPage::getFilePromptName() -{ - return tr("tokens"); -} - -void LoadTokensPage::retranslateUi() -{ - setTitle(tr("Tokens import")); - setSubTitle(tr("Please specify a compatible source for token data.")); - - urlRadioButton->setText(tr("Download URL:")); - fileRadioButton->setText(tr("Local file:")); - urlButton->setText(tr("Restore default URL")); - fileButton->setText(tr("Choose file...")); - - pathLabel->setText(tr("The token database will be saved at the following location:") + "
" + - SettingsCache::instance().getTokenDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); -} - -QString LoadSpoilersPage::getDefaultUrl() -{ - return SPOILERS_URL; -} - -QString LoadSpoilersPage::getCustomUrlSettingsKey() -{ - return "spoilersurl"; -} - -QString LoadSpoilersPage::getDefaultSavePath() -{ - return SettingsCache::instance().getTokenDatabasePath(); -} - -QString LoadSpoilersPage::getWindowTitle() -{ - return tr("Save spoiler database"); -} - -QString LoadSpoilersPage::getFileType() -{ - return tr("XML; spoiler database (*.xml)"); -} - -QString LoadSpoilersPage::getFilePromptName() -{ - return tr("spoiler"); -} - -void LoadSpoilersPage::retranslateUi() -{ - setTitle(tr("Spoilers import")); - setSubTitle(tr("Please specify a compatible source for spoiler data.")); - - urlRadioButton->setText(tr("Download URL:")); - fileRadioButton->setText(tr("Local file:")); - urlButton->setText(tr("Restore default URL")); - fileButton->setText(tr("Choose file...")); - - pathLabel->setText(tr("The spoiler database will be saved at the following location:") + "
" + - SettingsCache::instance().getSpoilerCardDatabasePath()); - defaultPathCheckBox->setText(tr("Save to a custom path (not recommended)")); -} \ No newline at end of file diff --git a/oracle/src/pages.h b/oracle/src/pages.h deleted file mode 100644 index 066cc2e1b..000000000 --- a/oracle/src/pages.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef COCKATRICE_PAGES_H -#define COCKATRICE_PAGES_H - -#include "pagetemplates.h" - -#include -#include -#include -#include -#include - -class QCheckBox; -class QGroupBox; -class QComboBox; -class QLabel; -class QLineEdit; -class QRadioButton; -class QProgressBar; -class QNetworkAccessManager; -class QTextEdit; -class QVBoxLayout; -class OracleImporter; -class QSettings; - -class IntroPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit IntroPage(QWidget *parent = nullptr); - void retranslateUi() override; - -private: - QStringList findQmFiles(); - QString languageName(const QString &lang); - -private: - QLabel *label, *languageLabel, *versionLabel; - QComboBox *languageBox; - -private slots: - void languageBoxChanged(int index); - -protected slots: - void initializePage() override; -}; - -class OutroPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit OutroPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - void initializePage() override; -}; - -class LoadSetsPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit LoadSetsPage(QWidget *parent = nullptr); - void retranslateUi() override; - -protected: - void initializePage() override; - bool validatePage() override; - void readSetsFromByteArray(QByteArray _data); - void readSetsFromByteArrayRef(QByteArray &_data); - void downloadSetsFile(const QUrl &url); - -private: - QRadioButton *urlRadioButton; - QRadioButton *fileRadioButton; - QLineEdit *urlLineEdit; - QLineEdit *fileLineEdit; - QPushButton *urlButton; - QPushButton *fileButton; - QLabel *progressLabel; - QProgressBar *progressBar; - - QFutureWatcher watcher; - QFuture future; - QByteArray jsonData; - -private slots: - void actLoadSetsFile(); - void actRestoreDefaultUrl(); - void actDownloadProgressSetsFile(qint64 received, qint64 total); - void actDownloadFinishedSetsFile(); - void importFinished(); - void zipDownloadFailed(const QString &message); -}; - -class SaveSetsPage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit SaveSetsPage(QWidget *parent = nullptr); - void retranslateUi() override; - -private: - QTextEdit *messageLog; - QCheckBox *defaultPathCheckBox; - QLabel *pathLabel; - QLabel *saveLabel; - -protected: - void initializePage() override; - void cleanupPage() override; - bool validatePage() override; - -private slots: - void updateTotalProgress(int cardsImported, int setIndex, const QString &setName); -}; - -class LoadSpoilersPage : public SimpleDownloadFilePage -{ - Q_OBJECT -public: - explicit LoadSpoilersPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - QString getDefaultUrl() override; - QString getCustomUrlSettingsKey() override; - QString getDefaultSavePath() override; - QString getWindowTitle() override; - QString getFileType() override; - QString getFilePromptName() override; -}; - -class LoadTokensPage : public SimpleDownloadFilePage -{ - Q_OBJECT -public: - explicit LoadTokensPage(QWidget * = nullptr) - { - } - void retranslateUi() override; - -protected: - QString getDefaultUrl() override; - QString getCustomUrlSettingsKey() override; - QString getDefaultSavePath() override; - QString getWindowTitle() override; - QString getFileType() override; - QString getFilePromptName() override; - void initializePage() override; -}; - -#endif // COCKATRICE_PAGES_H diff --git a/oracle/src/pagetemplates.cpp b/oracle/src/pagetemplates.cpp deleted file mode 100644 index 4a27fef30..000000000 --- a/oracle/src/pagetemplates.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "pagetemplates.h" - -#include "oraclewizard.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -SimpleDownloadFilePage::SimpleDownloadFilePage(QWidget *parent) : OracleWizardPage(parent) -{ - urlRadioButton = new QRadioButton(this); - fileRadioButton = new QRadioButton(this); - - urlLineEdit = new QLineEdit(this); - fileLineEdit = new QLineEdit(this); - - progressLabel = new QLabel(this); - progressBar = new QProgressBar(this); - - urlRadioButton->setChecked(true); - - urlButton = new QPushButton(this); - connect(urlButton, &QPushButton::clicked, this, &SimpleDownloadFilePage::actRestoreDefaultUrl); - - fileButton = new QPushButton(this); - connect(fileButton, &QPushButton::clicked, this, &SimpleDownloadFilePage::actLoadCardFile); - - defaultPathCheckBox = new QCheckBox(this); - pathLabel = new QLabel(this); - pathLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); - - auto *layout = new QGridLayout(this); - layout->addWidget(urlRadioButton, 0, 0); - layout->addWidget(urlLineEdit, 0, 1); - layout->addWidget(urlButton, 1, 1, Qt::AlignRight); - layout->addWidget(fileRadioButton, 2, 0); - layout->addWidget(fileLineEdit, 2, 1); - layout->addWidget(fileButton, 3, 1, Qt::AlignRight); - layout->addWidget(pathLabel, 4, 0, 1, 2); - layout->addWidget(defaultPathCheckBox, 5, 0, 1, 2); - layout->addWidget(progressLabel, 6, 0); - layout->addWidget(progressBar, 6, 1); - - setLayout(layout); -} - -void SimpleDownloadFilePage::initializePage() -{ - // get custom url from settings if any; otherwise use default url - urlLineEdit->setText(wizard()->settings->value(getCustomUrlSettingsKey(), getDefaultUrl()).toString()); - - progressLabel->hide(); - progressBar->hide(); -} - -void SimpleDownloadFilePage::actRestoreDefaultUrl() -{ - urlLineEdit->setText(getDefaultUrl()); -} - -void SimpleDownloadFilePage::actLoadCardFile() -{ - QFileDialog dialog(this, tr("Load %1 file").arg(getFilePromptName())); - dialog.setFileMode(QFileDialog::ExistingFile); - - QString extensions = "*.json *.xml"; -#ifdef HAS_ZLIB - extensions += " *.zip"; -#endif -#ifdef HAS_LZMA - extensions += " *.xz"; -#endif - dialog.setNameFilter(tr("%1 file (%1)").arg(getFilePromptName(), extensions)); - - if (!fileLineEdit->text().isEmpty() && QFile::exists(fileLineEdit->text())) { - dialog.selectFile(fileLineEdit->text()); - } - - if (!dialog.exec()) { - return; - } - - fileLineEdit->setText(dialog.selectedFiles().at(0)); -} - -bool SimpleDownloadFilePage::validatePage() -{ - // if data has already been downloaded, pass directly to the "save" step - if (!downloadData.isEmpty()) { - if (saveToFile()) { - return true; - } else { - wizard()->enableButtons(); - return false; - } - } - - // else, try to import sets - if (urlRadioButton->isChecked()) { - QUrl url = QUrl::fromUserInput(urlLineEdit->text()); - if (!url.isValid()) { - QMessageBox::critical(this, tr("Error"), tr("The provided URL is not valid: ") + url.toString()); - return false; - } - - progressLabel->setText(tr("Downloading (0MB)")); - // show an infinite progressbar - progressBar->setMaximum(0); - progressBar->setMinimum(0); - progressBar->setValue(0); - progressLabel->show(); - progressBar->show(); - - wizard()->disableButtons(); - downloadFile(url); - - } else if (fileRadioButton->isChecked()) { - QFile cardFile(fileLineEdit->text()); - if (!cardFile.exists()) { - QMessageBox::critical(this, tr("Error"), tr("Please choose a file.")); - return false; - } - - if (!cardFile.open(QIODevice::ReadOnly)) { - QMessageBox::critical(nullptr, tr("Error"), tr("Cannot open file '%1'.").arg(fileLineEdit->text())); - return false; - } - - downloadData = cardFile.readAll(); - wizard()->next(); - } - - return false; -} - -void SimpleDownloadFilePage::downloadFile(QUrl url) -{ - QNetworkReply *reply = wizard()->nam->get(QNetworkRequest(url)); - - connect(reply, &QNetworkReply::finished, this, &SimpleDownloadFilePage::actDownloadFinished); - connect(reply, &QNetworkReply::downloadProgress, this, &SimpleDownloadFilePage::actDownloadProgress); -} - -void SimpleDownloadFilePage::actDownloadProgress(qint64 received, qint64 total) -{ - if (total > 0) { - progressBar->setMaximum(static_cast(total)); - progressBar->setValue(static_cast(received)); - } - progressLabel->setText(tr("Downloading (%1MB)").arg((int)received / (1024 * 1024))); -} - -void SimpleDownloadFilePage::actDownloadFinished() -{ - // check for a reply - auto *reply = dynamic_cast(sender()); - QNetworkReply::NetworkError errorCode = reply->error(); - if (errorCode != QNetworkReply::NoError) { - QMessageBox::critical(this, tr("Error"), tr("Network error: %1.").arg(reply->errorString())); - wizard()->enableButtons(); - reply->deleteLater(); - return; - } - - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (statusCode == 301 || statusCode == 302) { - QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); - qDebug() << "following redirect url:" << redirectUrl.toString(); - downloadFile(redirectUrl); - reply->deleteLater(); - return; - } - - // save downloaded file url, but only if the user customized it and download was successful - if (urlLineEdit->text() != getDefaultUrl()) { - wizard()->settings->setValue(getCustomUrlSettingsKey(), urlLineEdit->text()); - } else { - wizard()->settings->remove(getCustomUrlSettingsKey()); - } - - downloadData = reply->readAll(); - reply->deleteLater(); - - wizard()->enableButtons(); - progressLabel->hide(); - progressBar->hide(); - - wizard()->next(); -} - -bool SimpleDownloadFilePage::saveToFile() -{ - QString defaultPath = getDefaultSavePath(); - QString windowName = getWindowTitle(); - QString fileType = getFileType(); - - QString fileName; - if (defaultPathCheckBox->isChecked()) { - fileName = QFileDialog::getSaveFileName(this, windowName, defaultPath, fileType); - } else { - fileName = defaultPath; - } - - if (fileName.isEmpty()) { - return false; - } - - QFileInfo fi(fileName); - QDir fileDir(fi.path()); - if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath())) { - return false; - } - - if (!internalSaveToFile(fileName)) { - QMessageBox::critical(this, tr("Error"), tr("The file could not be saved to %1").arg(fileName)); - return false; - } - - // clean saved downloadData - downloadData = QByteArray(); - return true; -} - -bool SimpleDownloadFilePage::internalSaveToFile(const QString &fileName) -{ - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - qDebug() << "File open (w) failed for" << fileName; - return false; - } - - if (file.write(downloadData) == -1) { - qDebug() << "File write (w) failed for" << fileName; - return false; - } - - file.close(); - return true; -} diff --git a/oracle/src/pagetemplates.h b/oracle/src/pagetemplates.h deleted file mode 100644 index 79dcdd632..000000000 --- a/oracle/src/pagetemplates.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef PAGETEMPLATES_H -#define PAGETEMPLATES_H - -#include - -class QFile; -class QRadioButton; -class OracleWizard; -class QCheckBox; -class QLabel; -class QLineEdit; -class QProgressBar; - -class OracleWizardPage : public QWizardPage -{ - Q_OBJECT -public: - explicit OracleWizardPage(QWidget *parent = nullptr) : QWizardPage(parent) - { - } - virtual void retranslateUi() = 0; - -signals: - void readyToContinue(); - -protected: - inline OracleWizard *wizard() - { - return (OracleWizard *)QWizardPage::wizard(); - }; -}; - -class SimpleDownloadFilePage : public OracleWizardPage -{ - Q_OBJECT -public: - explicit SimpleDownloadFilePage(QWidget *parent = nullptr); - -protected: - void initializePage() override; - bool validatePage() override; - void downloadFile(QUrl url); - virtual QString getDefaultUrl() = 0; - virtual QString getCustomUrlSettingsKey() = 0; - virtual QString getDefaultSavePath() = 0; - virtual QString getWindowTitle() = 0; - virtual QString getFileType() = 0; - virtual QString getFilePromptName() = 0; - bool saveToFile(); - bool internalSaveToFile(const QString &fileName); - -protected: - QByteArray downloadData; - QRadioButton *urlRadioButton; - QRadioButton *fileRadioButton; - QLineEdit *urlLineEdit; - QLineEdit *fileLineEdit; - QPushButton *urlButton; - QPushButton *fileButton; - QLabel *pathLabel; - QLabel *progressLabel; - QProgressBar *progressBar; - QCheckBox *defaultPathCheckBox; - -signals: - void parsedDataReady(); -private slots: - void actRestoreDefaultUrl(); - void actLoadCardFile(); - void actDownloadProgress(qint64 received, qint64 total); - void actDownloadFinished(); -}; - -#endif // PAGETEMPLATES_H diff --git a/oracle/src/parsehelpers.cpp b/oracle/src/parsehelpers.cpp deleted file mode 100644 index a97bf67c9..000000000 --- a/oracle/src/parsehelpers.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "parsehelpers.h" - -#include -#include - -/** - * Parses the card text to determine if the card should have the cipt tag - * - * The parsing logic is able to handle the following cases: - * - " enters tapped" - * - " enters tapped", if the card name starts with the shortname - * - "This enters tapped" - * - "..., it enters tapped" - * - Any naming scheme that appends a non-alphanumeric character plus extra text to the end of the name. - * (e.g. name is "Card Name_SET" or "Card Name (Set)" and text contains "Card Name enters tapped") - * - * However, it will still miss on certain cases: - * - shortnames that aren't the at the beginning of the card name - * - * Note that "...enters tapped unless..." returns false. - * - * @param name The name of the card - * @param text The oracle text of the card - */ -bool parseCipt(const QString &name, const QString &text) -{ - // Use precompiled regex to check if text is a possible candidate, and early return if not - static auto prelimCheck = QRegularExpression(" enters( the battlefield)? tapped(?! unless)"); - if (!prelimCheck.match(text).hasMatch()) { - return false; - } - - // Try to split shortname on most non-alphanumeric characters (including _) - auto isShortnameDivider = [](const QChar &c) { - return c == '_' || (!c.isLetterOrNumber() && c != '\'' && c != '\"'); - }; - - // Try all possible shortnames. - // This also handles the case of extra text appended at end. - QStringList possibleNames; - bool inAlphanumericPart = true; - for (int i = 0; i < name.length(); ++i) { - if (isShortnameDivider(name.at(i))) { - if (inAlphanumericPart) { - // only add to names on a "falling edge", in order to reduce the amount of redundant splits - possibleNames.append(QRegularExpression::escape(name.left(i))); - inAlphanumericPart = false; - } - } else { - inAlphanumericPart = true; - } - } - - // and the full name - possibleNames.append(QRegularExpression::escape(name)); - - QString subject = "(it|" // "..., it enters tapped" - "(T|t)his [^ ]+|" // "This enters tapped" - + possibleNames.join("|") + ")"; - - auto ciptPattern = QRegularExpression( - // cipt phrase is either first sentence of line, or is after a punctuation mark - "(^|(, |\\. ))" + subject + - // support old wording, and exclude the "unless" case - " enters( the battlefield)? tapped(?! unless)", - QRegularExpression::MultilineOption); - - return ciptPattern.match(text).hasMatch(); -} diff --git a/oracle/src/parsehelpers.h b/oracle/src/parsehelpers.h deleted file mode 100644 index 40a36c753..000000000 --- a/oracle/src/parsehelpers.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef PARSEHELPERS_H -#define PARSEHELPERS_H - -#include - -bool parseCipt(const QString &name, const QString &text); - -#endif // PARSEHELPERS_H diff --git a/oracle/src/qt-json/json.cpp b/oracle/src/qt-json/json.cpp deleted file mode 100644 index 2fffd0f70..000000000 --- a/oracle/src/qt-json/json.cpp +++ /dev/null @@ -1,573 +0,0 @@ -/* Copyright 2011 Eeli Reilin. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, - * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation - * are those of the authors and should not be interpreted as representing - * official policies, either expressed or implied, of Eeli Reilin. - */ - -/** - * \file json.cpp - */ - -#include "json.h" - -#include -#include - -namespace QtJson -{ - -static QString sanitizeString(QString str) -{ - str.replace(QLatin1String("\\"), QLatin1String("\\\\")); - str.replace(QLatin1String("\""), QLatin1String("\\\"")); - str.replace(QLatin1String("\b"), QLatin1String("\\b")); - str.replace(QLatin1String("\f"), QLatin1String("\\f")); - str.replace(QLatin1String("\n"), QLatin1String("\\n")); - str.replace(QLatin1String("\r"), QLatin1String("\\r")); - str.replace(QLatin1String("\t"), QLatin1String("\\t")); - return QString(QLatin1String("\"%1\"")).arg(str); -} - -static QByteArray join(const QList &list, const QByteArray &sep) -{ - QByteArray res; - for (const QByteArray &i : list) { - if (!res.isEmpty()) { - res += sep; - } - res += i; - } - return res; -} - -/** - * parse - */ -QVariant Json::parse(const QString &json) -{ - bool success = true; - return Json::parse(json, success); -} - -/** - * parse - */ -QVariant Json::parse(const QString &json, bool &success) -{ - success = true; - - // Return an empty QVariant if the JSON data is either null or empty - if (!json.isNull() || !json.isEmpty()) { - // We'll start from index 0 - int index = 0; - - // Parse the first value - QVariant value = Json::parseValue(json, index, success); - - // Return the parsed value - return value; - } else { - // Return the empty QVariant - return QVariant(); - } -} - -QByteArray Json::serialize(const QVariant &data) -{ - bool success = true; - return Json::serialize(data, success); -} - -QByteArray Json::serialize(const QVariant &data, bool &success) -{ - QByteArray str; - success = true; - - if (!data.isValid()) // invalid or null? - { - str = "null"; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if ((data.typeId() == QMetaType::Type::QVariantList) || - (data.typeId() == QMetaType::Type::QStringList)) // variant is a list? -#else - else if ((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list? -#endif - { - QList values; - const QVariantList list = data.toList(); - for (const QVariant &v : list) { - QByteArray serializedValue = serialize(v); - if (serializedValue.isNull()) { - success = false; - break; - } - values << serializedValue; - } - - str = "[ " + join(values, ", ") + " ]"; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if ((data.typeId() == QMetaType::Type::QVariantHash)) // variant is a list? -#else - else if (data.type() == QVariant::Hash) // variant is a hash? -#endif - { - const QVariantHash vhash = data.toHash(); - QHashIterator it(vhash); - str = "{ "; - QList pairs; - - while (it.hasNext()) { - it.next(); - QByteArray serializedValue = serialize(it.value()); - - if (serializedValue.isNull()) { - success = false; - break; - } - - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - - str += join(pairs, ", "); - str += " }"; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if ((data.typeId() == QMetaType::Type::QVariantMap)) // variant is a list? -#else - else if (data.type() == QVariant::Map) // variant is a map? -#endif - { - const QVariantMap vmap = data.toMap(); - QMapIterator it(vmap); - str = "{ "; - QList pairs; - while (it.hasNext()) { - it.next(); - QByteArray serializedValue = serialize(it.value()); - if (serializedValue.isNull()) { - success = false; - break; - } - pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue; - } - str += join(pairs, ", "); - str += " }"; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if ((data.typeId() == QMetaType::Type::QString) || - (data.typeId() == QMetaType::Type::QByteArray)) // variant is a list? -#else - else if ((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array? -#endif - { - str = sanitizeString(data.toString()).toUtf8(); - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if (data.typeId() == QMetaType::Type::Double) -#else - else if (data.type() == QVariant::Double) // double? -#endif - { - str = QByteArray::number(data.toDouble(), 'g', 20); - if (!str.contains(".") && !str.contains("e")) { - str += ".0"; - } - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if (data.typeId() == QMetaType::Type::Bool) -#else - else if (data.type() == QVariant::Bool) // boolean value? -#endif - { - str = data.toBool() ? "true" : "false"; - } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - else if (data.typeId() == QMetaType::Type::ULongLong) -#else - else if (data.type() == QVariant::ULongLong) // large unsigned number? -#endif - { - str = QByteArray::number(data.value()); - } else if (data.canConvert()) // any signed number? - { - str = QByteArray::number(data.value()); - } else if (data.canConvert()) { - str = QString::number(data.value()).toUtf8(); - } else if (data.canConvert()) // can value be converted to string? - { - // this will catch QDate, QDateTime, QUrl, ... - str = sanitizeString(data.toString()).toUtf8(); - } else { - success = false; - } - if (success) { - return str; - } else { - return QByteArray(); - } -} - -/** - * parseValue - */ -QVariant Json::parseValue(const QString &json, int &index, bool &success) -{ - // Determine what kind of data we should parse by - // checking out the upcoming token - switch (Json::lookAhead(json, index)) { - case JsonTokenString: - return Json::parseString(json, index, success); - case JsonTokenNumber: - return Json::parseNumber(json, index); - case JsonTokenCurlyOpen: - return Json::parseObject(json, index, success); - case JsonTokenSquaredOpen: - return Json::parseArray(json, index, success); - case JsonTokenTrue: - Json::nextToken(json, index); - return QVariant(true); - case JsonTokenFalse: - Json::nextToken(json, index); - return QVariant(false); - case JsonTokenNull: - Json::nextToken(json, index); - return QVariant(); - case JsonTokenNone: - break; - } - - // If there were no tokens, flag the failure and return an empty QVariant - success = false; - return QVariant(); -} - -/** - * parseObject - */ -QVariant Json::parseObject(const QString &json, int &index, bool &success) -{ - QVariantMap map; - int token; - - // Get rid of the whitespace and increment index - Json::nextToken(json, index); - - // Loop through all of the key/value pairs of the object - bool done = false; - while (!done) { - // Get the upcoming token - token = Json::lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantMap(); - } else if (token == JsonTokenComma) { - Json::nextToken(json, index); - } else if (token == JsonTokenCurlyClose) { - Json::nextToken(json, index); - return map; - } else { - // Parse the key/value pair's name - QString name = Json::parseString(json, index, success).toString(); - - if (!success) { - return QVariantMap(); - } - - // Get the next token - token = Json::nextToken(json, index); - - // If the next token is not a colon, flag the failure - // return an empty QVariant - if (token != JsonTokenColon) { - success = false; - return QVariant(QVariantMap()); - } - - // Parse the key/value pair's value - QVariant value = Json::parseValue(json, index, success); - - if (!success) { - return QVariantMap(); - } - - // Assign the value to the key in the map - map[name] = value; - } - } - - // Return the map successfully - return QVariant(map); -} - -/** - * parseArray - */ -QVariant Json::parseArray(const QString &json, int &index, bool &success) -{ - QVariantList list; - - Json::nextToken(json, index); - - bool done = false; - while (!done) { - int token = Json::lookAhead(json, index); - - if (token == JsonTokenNone) { - success = false; - return QVariantList(); - } else if (token == JsonTokenComma) { - Json::nextToken(json, index); - } else if (token == JsonTokenSquaredClose) { - Json::nextToken(json, index); - break; - } else { - QVariant value = Json::parseValue(json, index, success); - - if (!success) { - return QVariantList(); - } - - list.push_back(value); - } - } - - return QVariant(list); -} - -/** - * parseString - */ -QVariant Json::parseString(const QString &json, int &index, bool &success) -{ - QString s; - QChar c; - - Json::eatWhitespace(json, index); - - c = json[index++]; - - bool complete = false; - while (!complete) { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - complete = true; - break; - } else if (c == '\\') { - if (index == json.size()) { - break; - } - - c = json[index++]; - - if (c == '\"') { - s.append('\"'); - } else if (c == '\\') { - s.append('\\'); - } else if (c == '/') { - s.append('/'); - } else if (c == 'b') { - s.append('\b'); - } else if (c == 'f') { - s.append('\f'); - } else if (c == 'n') { - s.append('\n'); - } else if (c == 'r') { - s.append('\r'); - } else if (c == 't') { - s.append('\t'); - } else if (c == 'u') { - int remainingLength = json.size() - index; - - if (remainingLength >= 4) { - QString unicodeStr = json.mid(index, 4); - - int symbol = unicodeStr.toInt(0, 16); - - s.append(QChar(symbol)); - - index += 4; - } else { - break; - } - } - } else { - s.append(c); - } - } - - if (!complete) { - success = false; - return QVariant(); - } - - return QVariant(s); -} - -/** - * parseNumber - */ -QVariant Json::parseNumber(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - int lastIndex = Json::lastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - QString numberStr; - - numberStr = json.mid(index, charLength); - - index = lastIndex + 1; - - if (numberStr.contains('.')) { - return QVariant(numberStr.toDouble(NULL)); - } else if (numberStr.startsWith('-')) { - return QVariant(numberStr.toLongLong(NULL)); - } else { - return QVariant(numberStr.toULongLong(NULL)); - } -} - -/** - * lastIndexOfNumber - */ -int Json::lastIndexOfNumber(const QString &json, int index) -{ - static const QString numericCharacters("0123456789+-.eE"); - int lastIndex; - - for (lastIndex = index; lastIndex < json.size(); lastIndex++) { - if (numericCharacters.indexOf(json[lastIndex]) == -1) { - break; - } - } - - return lastIndex - 1; -} - -/** - * eatWhitespace - */ -void Json::eatWhitespace(const QString &json, int &index) -{ - static const QString whitespaceChars(" \t\n\r"); - for (; index < json.size(); index++) { - if (whitespaceChars.indexOf(json[index]) == -1) { - break; - } - } -} - -/** - * lookAhead - */ -int Json::lookAhead(const QString &json, int index) -{ - int saveIndex = index; - return Json::nextToken(json, saveIndex); -} - -/** - * nextToken - */ -int Json::nextToken(const QString &json, int &index) -{ - Json::eatWhitespace(json, index); - - if (index == json.size()) { - return JsonTokenNone; - } - - QChar c = json[index]; - index++; - switch (c.toLatin1()) { - case '{': - return JsonTokenCurlyOpen; - case '}': - return JsonTokenCurlyClose; - case '[': - return JsonTokenSquaredOpen; - case ']': - return JsonTokenSquaredClose; - case ',': - return JsonTokenComma; - case '"': - return JsonTokenString; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return JsonTokenNumber; - case ':': - return JsonTokenColon; - } - - index--; - - int remainingLength = json.size() - index; - - // True - if (remainingLength >= 4) { - if (json[index] == 't' && json[index + 1] == 'r' && json[index + 2] == 'u' && json[index + 3] == 'e') { - index += 4; - return JsonTokenTrue; - } - } - - // False - if (remainingLength >= 5) { - if (json[index] == 'f' && json[index + 1] == 'a' && json[index + 2] == 'l' && json[index + 3] == 's' && - json[index + 4] == 'e') { - index += 5; - return JsonTokenFalse; - } - } - - // Null - if (remainingLength >= 4) { - if (json[index] == 'n' && json[index + 1] == 'u' && json[index + 2] == 'l' && json[index + 3] == 'l') { - index += 4; - return JsonTokenNull; - } - } - - return JsonTokenNone; -} - -} // namespace QtJson diff --git a/oracle/src/zip/unzip.cpp b/oracle/src/zip/unzip.cpp index 8e52ece18..da250cee4 100755 --- a/oracle/src/zip/unzip.cpp +++ b/oracle/src/zip/unzip.cpp @@ -1,1425 +1,1430 @@ -/**************************************************************************** -** Filename: unzip.cpp -** Last updated [dd/mm/yyyy]: 08/07/2010 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "unzip.h" -#include "unzip_p.h" -#include "zipentry_p.h" - -#include -#include -#include -#include -#include - -// You can remove this #include if you replace the qDebug() statements. -#include - -/*! - \class UnZip unzip.h - - \brief PKZip 2.0 file decompression. - Compatibility with later versions is not ensured as they may use - unsupported compression algorithms. - Versions after 2.7 may have an incompatible header format and thus be - completely incompatible. -*/ - -/*! \enum UnZip::ErrorCode The result of a decompression operation. - \value UnZip::Ok No error occurred. - \value UnZip::ZlibInit Failed to init or load the zlib library. - \value UnZip::ZlibError The zlib library returned some error. - \value UnZip::OpenFailed Unable to create or open a device. - \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted. - \value UnZip::Corrupted Corrupted or invalid zip archive. - \value UnZip::WrongPassword Unable to decrypt a password protected file. - \value UnZip::NoOpenArchive No archive has been opened yet. - \value UnZip::FileNotFound Unable to find the requested file in the archive. - \value UnZip::ReadFailed Reading of a file failed. - \value UnZip::WriteFailed Writing of a file failed. - \value UnZip::SeekFailed Seek failed. - \value UnZip::CreateDirFailed Could not create a directory. - \value UnZip::InvalidDevice A null device has been passed as parameter. - \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive. - \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted. - - \value UnZip::Skip Internal use only. - \value UnZip::SkipAll Internal use only. -*/ - -/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods. - \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files. - \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory. - \value UnZip::VerifyOnly Doesn't actually extract files. - \value UnZip::NoSilentDirectoryCreation Doesn't attempt to silently create missing output directories. -*/ - -//! Local header size (excluding signature, excluding variable length fields) -#define UNZIP_LOCAL_HEADER_SIZE 26 -//! Central Directory file entry size (excluding signature, excluding variable length fields) -#define UNZIP_CD_ENTRY_SIZE_NS 42 -//! Data descriptor size (excluding signature) -#define UNZIP_DD_SIZE 12 -//! End Of Central Directory size (including signature, excluding variable length fields) -#define UNZIP_EOCD_SIZE 22 -//! Local header entry encryption header size -#define UNZIP_LOCAL_ENC_HEADER_SIZE 12 - -// Some offsets inside a CD record (excluding signature) -#define UNZIP_CD_OFF_VERSION_MADE 0 -#define UNZIP_CD_OFF_VERSION 2 -#define UNZIP_CD_OFF_GPFLAG 4 -#define UNZIP_CD_OFF_CMETHOD 6 -#define UNZIP_CD_OFF_MODT 8 -#define UNZIP_CD_OFF_MODD 10 -#define UNZIP_CD_OFF_CRC32 12 -#define UNZIP_CD_OFF_CSIZE 16 -#define UNZIP_CD_OFF_USIZE 20 -#define UNZIP_CD_OFF_NAMELEN 24 -#define UNZIP_CD_OFF_XLEN 26 -#define UNZIP_CD_OFF_COMMLEN 28 -#define UNZIP_CD_OFF_LHOFFSET 38 - -// Some offsets inside a local header record (excluding signature) -#define UNZIP_LH_OFF_VERSION 0 -#define UNZIP_LH_OFF_GPFLAG 2 -#define UNZIP_LH_OFF_CMETHOD 4 -#define UNZIP_LH_OFF_MODT 6 -#define UNZIP_LH_OFF_MODD 8 -#define UNZIP_LH_OFF_CRC32 10 -#define UNZIP_LH_OFF_CSIZE 14 -#define UNZIP_LH_OFF_USIZE 18 -#define UNZIP_LH_OFF_NAMELEN 22 -#define UNZIP_LH_OFF_XLEN 24 - -// Some offsets inside a data descriptor record (excluding signature) -#define UNZIP_DD_OFF_CRC32 0 -#define UNZIP_DD_OFF_CSIZE 4 -#define UNZIP_DD_OFF_USIZE 8 - -// Some offsets inside a EOCD record -#define UNZIP_EOCD_OFF_ENTRIES 6 -#define UNZIP_EOCD_OFF_CDOFF 12 -#define UNZIP_EOCD_OFF_COMMLEN 16 - -/*! - Max version handled by this API. - 0x14 = 2.0 --> full compatibility only up to this version; - later versions use unsupported features -*/ -#define UNZIP_VERSION 0x14 - -//! CRC32 routine -#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) - -OSDAB_BEGIN_NAMESPACE(Zip) - - -/************************************************************************ - ZipEntry -*************************************************************************/ - -/*! - ZipEntry constructor - initialize data. Type is set to File. -*/ -UnZip::ZipEntry::ZipEntry() -{ - compressedSize = uncompressedSize = crc32 = 0; - compression = NoCompression; - type = File; - encrypted = false; -} - - -/************************************************************************ - Private interface -*************************************************************************/ - -//! \internal -UnzipPrivate::UnzipPrivate() : - password(), - skipAllEncrypted(false), - headers(0), - device(0), - file(0), - uBuffer(0), - crcTable(0), - cdOffset(0), - eocdOffset(0), - cdEntryCount(0), - unsupportedEntryCount(0), - comment() -{ - uBuffer = (unsigned char*) buffer1; - crcTable = (quint32*) get_crc_table(); -} - -//! \internal -void UnzipPrivate::deviceDestroyed(QObject*) -{ - qDebug("Unexpected device destruction detected."); - do_closeArchive(); -} - -//! \internal Parses a Zip archive. -UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) -{ - Q_ASSERT(!device); - Q_ASSERT(dev); - - if (!(dev->isOpen() || dev->open(QIODevice::ReadOnly))) { - qDebug() << "Unable to open device for reading"; - return UnZip::OpenFailed; - } - - device = dev; - if (device != file) - connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); - - UnZip::ErrorCode ec; - - ec = seekToCentralDirectory(); - if (ec != UnZip::Ok) { - closeArchive(); - return ec; - } - - //! \todo Ignore CD entry count? CD may be corrupted. - if (cdEntryCount == 0) { - return UnZip::Ok; - } - - bool continueParsing = true; - - while (continueParsing) { - if (device->read(buffer1, 4) != 4) { - if (headers) { - qDebug() << "Corrupted zip archive. Some files might be extracted."; - ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted; - break; - } else { - closeArchive(); - qDebug() << "Corrupted or invalid zip archive. Closing."; - ec = UnZip::Corrupted; - break; - } - } - - if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) ) - break; - - if ((ec = parseCentralDirectoryRecord()) != UnZip::Ok) - break; - } - - if (ec != UnZip::Ok) - closeArchive(); - - return ec; -} - -/* - \internal Parses a local header record and makes some consistency check - with the information stored in the Central Directory record for this entry - that has been previously parsed. - - local file header signature 4 bytes (0x04034b50) - version needed to extract 2 bytes - general purpose bit flag 2 bytes - compression method 2 bytes - last mod file time 2 bytes - last mod file date 2 bytes - crc-32 4 bytes - compressed size 4 bytes - uncompressed size 4 bytes - file name length 2 bytes - extra field length 2 bytes - - file name (variable size) - extra field (variable size) -*/ -//! \todo Optional consistency check (as a ExtractionOptions flag) -UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry) -{ - Q_ASSERT(device); - - if (!device->seek(entry.lhOffset)) - return UnZip::SeekFailed; - - // Test signature - if (device->read(buffer1, 4) != 4) - return UnZip::ReadFailed; - - if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04)) - return UnZip::InvalidArchive; - - if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE) - return UnZip::ReadFailed; - - /* - Check 3rd general purpose bit flag. - - "bit 3: If this bit is set, the fields crc-32, compressed size - and uncompressed size are set to zero in the local - header. The correct values are put in the data descriptor - immediately following the compressed data." - */ - bool hasDataDescriptor = entry.hasDataDescriptor(); - bool checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD); - - if (!checkFailed) - checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG]; - if (!checkFailed) - checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1]; - if (!checkFailed) - checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT]; - if (!checkFailed) - checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1]; - if (!checkFailed) - checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD]; - if (!checkFailed) - checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1]; - if (!hasDataDescriptor) - { - if (!checkFailed) - checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32); - if (!checkFailed) - checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE); - if (!checkFailed) - checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE); - } - - if (checkFailed) - return UnZip::HeaderConsistencyError; - - // Check filename - quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN); - if (szName == 0) - return UnZip::HeaderConsistencyError; - - if (device->read(buffer2, szName) != szName) - return UnZip::ReadFailed; - - QString filename = QString::fromLatin1(buffer2, szName); - if (filename != path) { - qDebug() << "Filename in local header mismatches."; - return UnZip::HeaderConsistencyError; - } - - // Skip extra field - quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN); - if (szExtra != 0) { - if (!device->seek(device->pos() + szExtra)) - return UnZip::SeekFailed; - } - - entry.dataOffset = device->pos(); - - if (hasDataDescriptor) { - /* - The data descriptor has this OPTIONAL signature: PK\7\8 - We try to skip the compressed data relying on the size set in the - Central Directory record. - */ - if (!device->seek(device->pos() + entry.szComp)) - return UnZip::SeekFailed; - - // Read 4 bytes and check if there is a data descriptor signature - if (device->read(buffer2, 4) != 4) - return UnZip::ReadFailed; - - bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08; - if (hasSignature) { - if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE) - return UnZip::ReadFailed; - } else { - if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4) - return UnZip::ReadFailed; - } - - // DD: crc, compressed size, uncompressed size - if ( - entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) || - entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) || - entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE) - ) - return UnZip::HeaderConsistencyError; - } - - return UnZip::Ok; -} - -/*! \internal Attempts to find the start of the central directory record. - - We seek the file back until we reach the "End Of Central Directory" - signature PK\5\6. - - end of central dir signature 4 bytes (0x06054b50) - number of this disk 2 bytes - number of the disk with the - start of the central directory 2 bytes - total number of entries in the - central directory on this disk 2 bytes - total number of entries in - the central directory 2 bytes - size of the central directory 4 bytes - offset of start of central - directory with respect to - the starting disk number 4 bytes - .ZIP file comment length 2 bytes - --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE --- - .ZIP file comment (variable size) -*/ -UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory() -{ - Q_ASSERT(device); - - qint64 length = device->size(); - qint64 offset = length - UNZIP_EOCD_SIZE; - - if (length < UNZIP_EOCD_SIZE) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - - if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) - return UnZip::ReadFailed; - - bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06); - - if (eocdFound) { - // Zip file has no comment (the only variable length field in the EOCD record) - eocdOffset = offset; - } else { - qint64 read; - char* p = 0; - - offset -= UNZIP_EOCD_SIZE; - - if (offset <= 0) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - - while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0) { - if ( (p = strstr(buffer1, "PK\5\6")) != 0) { - // Seek to the start of the EOCD record so we can read it fully - // Yes... we could simply read the missing bytes and append them to the buffer - // but this is far easier so heck it! - device->seek( offset + (p - buffer1) ); - eocdFound = true; - eocdOffset = offset + (p - buffer1); - - // Read EOCD record - if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) - return UnZip::ReadFailed; - - break; - } - - // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here. - offset -= 1 /*UNZIP_EOCD_SIZE*/; - if (offset <= 0) - return UnZip::InvalidArchive; - - if (!device->seek( offset )) - return UnZip::SeekFailed; - } - } - - if (!eocdFound) - return UnZip::InvalidArchive; - - // Parse EOCD to locate CD offset - offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4); - - cdOffset = offset; - - cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4); - - quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4); - if (commentLength != 0) { - QByteArray c = device->read(commentLength); - if (c.size() != commentLength) - return UnZip::ReadFailed; - - comment = c; - } - - // Seek to the start of the CD record - if (!device->seek( cdOffset )) - return UnZip::SeekFailed; - - return UnZip::Ok; -} - -/*! - \internal Parses a central directory record. - - Central Directory record structure: - - [file header 1] - . - . - . - [file header n] - [digital signature] // PKZip 6.2 or later only - - File header: - - central file header signature 4 bytes (0x02014b50) - version made by 2 bytes - version needed to extract 2 bytes - general purpose bit flag 2 bytes - compression method 2 bytes - last mod file time 2 bytes - last mod file date 2 bytes - crc-32 4 bytes - compressed size 4 bytes - uncompressed size 4 bytes - file name length 2 bytes - extra field length 2 bytes - file comment length 2 bytes - disk number start 2 bytes - internal file attributes 2 bytes - external file attributes 4 bytes - relative offset of local header 4 bytes - - file name (variable size) - extra field (variable size) - file comment (variable size) -*/ -UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord() -{ - Q_ASSERT(device); - - // Read CD record - if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS) - return UnZip::ReadFailed; - - bool skipEntry = false; - - // Get compression type so we can skip non compatible algorithms - quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD); - - // Get variable size fields length so we can skip the whole record - // if necessary - quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN); - quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN); - quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN); - - quint32 skipLength = szName + szExtra + szComment; - - UnZip::ErrorCode ec = UnZip::Ok; - - if ((compMethod != 0) && (compMethod != 8)) { - qDebug() << "Unsupported compression method. Skipping file."; - skipEntry = true; - } - - if (!skipEntry && szName == 0) { - qDebug() << "Skipping file with no name."; - skipEntry = true; - } - - QString filename; - if (device->read(buffer2, szName) != szName) { - ec = UnZip::ReadFailed; - skipEntry = true; - } else { - filename = QString::fromLatin1(buffer2, szName); - } - - // Unsupported features if version is bigger than UNZIP_VERSION - if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION) { - QString v = QString::number(buffer1[UNZIP_CD_OFF_VERSION]); - if (v.length() == 2) - v.insert(1, QLatin1Char('.')); - v = QString::fromLatin1("Unsupported PKZip version (%1). Skipping file: %2") - .arg(v, filename.isEmpty() ? QString::fromLatin1("") : filename); - qDebug() << v.toLatin1().constData(); - skipEntry = true; - } - - if (skipEntry) { - if (ec == UnZip::Ok) { - if (!device->seek( device->pos() + skipLength )) - ec = UnZip::SeekFailed; - unsupportedEntryCount++; - } - - return ec; - } - - ZipEntryP* h = new ZipEntryP; - h->compMethod = compMethod; - - h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG]; - h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1]; - - h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT]; - h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1]; - - h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD]; - h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1]; - - h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32); - h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE); - h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE); - - // Skip extra field (if any) - if (szExtra != 0) { - if (!device->seek( device->pos() + szExtra )) { - delete h; - return UnZip::SeekFailed; - } - } - - // Read comment field (if any) - if (szComment != 0) { - if (device->read(buffer2, szComment) != szComment) { - delete h; - return UnZip::ReadFailed; - } - - h->comment = QString::fromLatin1(buffer2, szComment); - } - - h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET); - - if (!headers) - headers = new QMap(); - headers->insert(filename, h); - - return UnZip::Ok; -} - -//! \internal Closes the archive and resets the internal status. -void UnzipPrivate::closeArchive() -{ - if (!device) { - Q_ASSERT(!file); - return; - } - - if (device != file) - disconnect(device, 0, this, 0); - - do_closeArchive(); -} - -//! \internal -void UnzipPrivate::do_closeArchive() -{ - skipAllEncrypted = false; - - if (headers) { - if (headers) - qDeleteAll(*headers); - delete headers; - headers = 0; - } - - device = 0; - - if (file) - delete file; - file = 0; - - cdOffset = eocdOffset = 0; - cdEntryCount = 0; - unsupportedEntryCount = 0; - - comment.clear(); -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, - const QDir& dir, UnZip::ExtractionOptions options) -{ - QString name(path); - QString dirname; - QString directory; - - const bool verify = (options & UnZip::VerifyOnly); - const int pos = name.lastIndexOf('/'); - - // This entry is for a directory - if (pos == name.length() - 1) { - if (verify) - return UnZip::Ok; - - if (options & UnZip::SkipPaths) - return UnZip::Ok; - - directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name)); - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create directory: %1").arg(directory); - return UnZip::CreateDirFailed; - } - - return UnZip::Ok; - } - - // Extract path from entry - if (verify) { - return extractFile(path, entry, 0, options); - } - - if (pos > 0) { - // get directory part - dirname = name.left(pos); - if (options & UnZip::SkipPaths) { - directory = dir.absolutePath(); - } else { - directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname)); - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create directory: %1").arg(directory); - return UnZip::CreateDirFailed; - } - } - name = name.right(name.length() - pos - 1); - } else { - directory = dir.absolutePath(); - } - - const bool silentDirectoryCreation = !(options & UnZip::NoSilentDirectoryCreation); - if (silentDirectoryCreation) { - if (!createDirectory(directory)) { - qDebug() << QString("Unable to create output directory %1").arg(directory); - return UnZip::CreateDirFailed; - } - } - - name = QString("%1/%2").arg(directory).arg(name); - - QFile outFile(name); - if (!outFile.open(QIODevice::WriteOnly)) { - qDebug() << QString("Unable to open %1 for writing").arg(name); - return UnZip::OpenFailed; - } - - UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options); - outFile.close(); - - const QDateTime lastModified = convertDateTime(entry.modDate, entry.modTime); - const bool setTimeOk = OSDAB_ZIP_MANGLE(setFileTimestamp)(name, lastModified); - if (!setTimeOk) { - qDebug() << QString("Unable to set last modified time on file: %1").arg(name); - } - - if (ec != UnZip::Ok) { - if (!outFile.remove()) - qDebug() << QString("Unable to remove corrupted file: %1").arg(name); - } - - return ec; -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::extractStoredFile( - const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, - UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - const bool isEncrypted = keys != 0; - - uInt rep = szComp / UNZIP_READ_BUFFER; - uInt rem = szComp % UNZIP_READ_BUFFER; - uInt cur = 0; - - // extract data - qint64 read; - quint64 tot = 0; - - while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 ) { - if (isEncrypted) - decryptBytes(*keys, buffer1, read); - - myCRC = crc32(myCRC, uBuffer, read); - if (!verify) { - if (outDev->write(buffer1, read) != read) - return UnZip::WriteFailed; - } - - cur++; - tot += read; - if (tot == szComp) - break; - } - - return (read < 0) - ? UnZip::ReadFailed - : UnZip::Ok; -} - -//! \internal -UnZip::ErrorCode UnzipPrivate::inflateFile( - const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, - UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - const bool isEncrypted = keys != 0; - Q_ASSERT(verify ? true : outDev != 0); - - uInt rep = szComp / UNZIP_READ_BUFFER; - uInt rem = szComp % UNZIP_READ_BUFFER; - uInt cur = 0; - - // extract data - qint64 read; - - /* Allocate inflate state */ - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - zstr.next_in = Z_NULL; - zstr.avail_in = 0; - - int zret; - - // Use inflateInit2 with negative windowBits to get raw decompression - if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK ) - return UnZip::ZlibError; - - int szDecomp; - - // Decompress until deflate stream ends or end of file - do { - read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem); - if (!read) - break; - - if (read < 0) { - (void)inflateEnd(&zstr); - return UnZip::ReadFailed; - } - - if (isEncrypted) - decryptBytes(*keys, buffer1, read); - - cur++; - - zstr.avail_in = (uInt) read; - zstr.next_in = (Bytef*) buffer1; - - // Run inflate() on input until output buffer not full - do { - zstr.avail_out = UNZIP_READ_BUFFER; - zstr.next_out = (Bytef*) buffer2;; - - zret = inflate(&zstr, Z_NO_FLUSH); - - switch (zret) { - case Z_NEED_DICT: - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&zstr); - return UnZip::WriteFailed; - default: - ; - } - - szDecomp = UNZIP_READ_BUFFER - zstr.avail_out; - if (!verify) { - if (outDev->write(buffer2, szDecomp) != szDecomp) { - inflateEnd(&zstr); - return UnZip::ZlibError; - } - } - - myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp); - - } while (zstr.avail_out == 0); - - } while (zret != Z_STREAM_END); - - inflateEnd(&zstr); - return UnZip::Ok; -} - -//! \internal \p outDev is null if the VerifyOnly option is set -UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, - QIODevice* outDev, UnZip::ExtractionOptions options) -{ - const bool verify = (options & UnZip::VerifyOnly); - - Q_UNUSED(options); - Q_ASSERT(device); - Q_ASSERT(verify ? true : outDev != 0); - - if (!entry.lhEntryChecked) { - UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry); - entry.lhEntryChecked = true; - if (ec != UnZip::Ok) - return ec; - } - - if (!device->seek(entry.dataOffset)) - return UnZip::SeekFailed; - - // Encryption keys - quint32 keys[3]; - quint32 szComp = entry.szComp; - if (entry.isEncrypted()) { - UnZip::ErrorCode e = testPassword(keys, path, entry); - if (e != UnZip::Ok) - { - qDebug() << QString("Unable to decrypt %1").arg(path); - return e; - }//! Encryption header size - szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size - } - - if (szComp == 0) { - if (entry.crc != 0) - return UnZip::Corrupted; - return UnZip::Ok; - } - - quint32 myCRC = crc32(0L, Z_NULL, 0); - quint32* k = keys; - - UnZip::ErrorCode ec = UnZip::Ok; - if (entry.compMethod == 0) { - ec = extractStoredFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); - } else if (entry.compMethod == 8) { - ec = inflateFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); - } - - if (ec == UnZip::Ok && myCRC != entry.crc) - return UnZip::Corrupted; - - return UnZip::Ok; -} - -//! \internal Creates a new directory and all the needed parent directories. -bool UnzipPrivate::createDirectory(const QString& path) -{ - QDir d(path); - if (!d.exists() && !d.mkpath(path)) { - qDebug() << QString("Unable to create directory: %1").arg(path); - return false; - } - - return true; -} - -/*! - \internal Reads an quint32 (4 bytes) from a byte array starting at given offset. -*/ -quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const -{ - quint32 res = (quint32) data[offset]; - res |= (((quint32)data[offset+1]) << 8); - res |= (((quint32)data[offset+2]) << 16); - res |= (((quint32)data[offset+3]) << 24); - - return res; -} - -/*! - \internal Reads an quint64 (8 bytes) from a byte array starting at given offset. -*/ -quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const -{ - quint64 res = (quint64) data[offset]; - res |= (((quint64)data[offset+1]) << 8); - res |= (((quint64)data[offset+2]) << 16); - res |= (((quint64)data[offset+3]) << 24); - res |= (((quint64)data[offset+1]) << 32); - res |= (((quint64)data[offset+2]) << 40); - res |= (((quint64)data[offset+3]) << 48); - res |= (((quint64)data[offset+3]) << 56); - - return res; -} - -/*! - \internal Reads an quint16 (2 bytes) from a byte array starting at given offset. -*/ -quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const -{ - return (quint16) data[offset] | (((quint16)data[offset+1]) << 8); -} - -/*! - \internal Return the next byte in the pseudo-random sequence - */ -int UnzipPrivate::decryptByte(quint32 key2) const -{ - quint16 temp = ((quint16)(key2) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -/*! - \internal Update the encryption keys with the next byte of plain text - */ -void UnzipPrivate::updateKeys(quint32* keys, int c) const -{ - keys[0] = CRC32(keys[0], c); - keys[1] += keys[0] & 0xff; - keys[1] = keys[1] * 134775813L + 1; - keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24); -} - -/*! - \internal Initialize the encryption keys and the random header according to - the given password. - */ -void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const -{ - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - - QByteArray pwdBytes = pwd.toLatin1(); - int sz = pwdBytes.size(); - const char* ascii = pwdBytes.data(); - - for (int i = 0; i < sz; ++i) - updateKeys(keys, (int)ascii[i]); -} - -/*! - \internal Attempts to test a password without actually extracting a file. - The \p file parameter can be used in the user interface or for debugging purposes - as it is the name of the encrypted file for wich the password is being tested. -*/ -UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString&_file, const ZipEntryP& header) -{ - Q_UNUSED(_file); - Q_ASSERT(device); - - // read encryption keys - if (device->read(buffer1, 12) != 12) - return UnZip::Corrupted; - - // Replace this code if you want to i.e. call some dialog and ask the user for a password - initKeys(password, keys); - if (testKeys(header, keys)) - return UnZip::Ok; - - return UnZip::Skip; -} - -/*! - \internal Tests a set of keys on the encryption header. -*/ -bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys) -{ - char lastByte; - - // decrypt encryption header - for (int i = 0; i < 11; ++i) - updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2])); - updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2])); - - // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time - // with no extended header we have to check the crc high-order byte - char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24; - - return (lastByte == c); -} - -/*! - \internal Decrypts an array of bytes long \p read. -*/ -void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read) -{ - for (int i = 0; i < (int)read; ++i) - updateKeys(keys, buffer[i] ^= decryptByte(keys[2])); -} - -/*! - \internal Converts date and time values from ZIP format to a QDateTime object. -*/ -QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const -{ - QDateTime dt; - - // Usual PKZip low-byte to high-byte order - - // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day - quint16 year = (date[1] >> 1) & 127; - quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7); - quint16 day = date[0] & 31; - - // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision - quint16 hour = (time[1] >> 3) & 31; - quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7); - quint16 seconds = (time[0] & 31) * 2; - - dt.setDate(QDate(1980 + year, month, day)); - dt.setTime(QTime(hour, minutes, seconds)); - return dt; -} - - -/************************************************************************ - Public interface -*************************************************************************/ - -/*! - Creates a new Zip file decompressor. -*/ -UnZip::UnZip() : d(new UnzipPrivate) -{ -} - -/*! - Closes any open archive and releases used resources. -*/ -UnZip::~UnZip() -{ - closeArchive(); - delete d; -} - -/*! - Returns true if there is an open archive. -*/ -bool UnZip::isOpen() const -{ - return d->device; -} - -/*! - Opens a zip archive and reads the files list. Closes any previously opened archive. -*/ -UnZip::ErrorCode UnZip::openArchive(const QString& filename) -{ - closeArchive(); - - // closeArchive will destroy the file - d->file = new QFile(filename); - - if (!d->file->exists()) { - delete d->file; - d->file = 0; - return UnZip::FileNotFound; - } - - if (!d->file->open(QIODevice::ReadOnly)) { - delete d->file; - d->file = 0; - return UnZip::OpenFailed; - } - - return d->openArchive(d->file); -} - -/*! - Opens a zip archive and reads the entries list. - Closes any previously opened archive. - \warning The class takes DOES NOT take ownership of the device. -*/ -UnZip::ErrorCode UnZip::openArchive(QIODevice* device) -{ - closeArchive(); - - if (!device) { - qDebug() << "Invalid device."; - return UnZip::InvalidDevice; - } - - return d->openArchive(device); -} - -/*! - Closes the archive and releases all the used resources (like cached passwords). -*/ -void UnZip::closeArchive() -{ - d->closeArchive(); -} - -QString UnZip::archiveComment() const -{ - return d->comment; -} - -/*! - Returns a locale translated error string for a given error code. -*/ -QString UnZip::formatError(UnZip::ErrorCode c) const -{ - switch (c) - { - case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break; - case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break; - case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break; - case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break; - case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break; - case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break; - case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break; - case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break; - case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break; - case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break; - case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break; - case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break; - case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break; - case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break; - case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break; - case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break; - default: ; - } - - return QCoreApplication::translate("UnZip", "Unknown error."); -} - -/*! - Returns true if the archive contains a file with the given path and name. -*/ -bool UnZip::contains(const QString& file) const -{ - return d->headers ? d->headers->contains(file) : false; -} - -/*! - Returns complete paths of files and directories in this archive. -*/ -QStringList UnZip::fileList() const -{ - return d->headers ? d->headers->keys() : QStringList(); -} - -/*! - Returns information for each (correctly parsed) entry of this archive. -*/ -QList UnZip::entryList() const -{ - QList list; - if (!d->headers) - return list; - - for (QMap::ConstIterator it = d->headers->constBegin(); - it != d->headers->constEnd(); ++it) { - const ZipEntryP* entry = it.value(); - Q_ASSERT(entry != 0); - - ZipEntry z; - - z.filename = it.key(); - if (!entry->comment.isEmpty()) - z.comment = entry->comment; - z.compressedSize = entry->szComp; - z.uncompressedSize = entry->szUncomp; - z.crc32 = entry->crc; - z.lastModified = d->convertDateTime(entry->modDate, entry->modTime); - - z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression; - z.type = z.filename.endsWith("/") ? Directory : File; - - z.encrypted = entry->isEncrypted(); - - list.append(z); - } - - return list; -} - -/*! - Extracts the whole archive to a directory. -*/ -UnZip::ErrorCode UnZip::verifyArchive() -{ - return extractAll(QDir(), VerifyOnly); -} - -/*! - Extracts the whole archive to a directory. -*/ -UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options) -{ - return extractAll(QDir(dirname), options); -} - -/*! - Extracts the whole archive to a directory. - Stops extraction at the first error. -*/ -UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options) -{ - // this should only happen if we didn't call openArchive() yet - if (!d->device) - return NoOpenArchive; - - if (!d->headers) - return Ok; - - ErrorCode ec = Ok; - - QMap::ConstIterator it = d->headers->constBegin(); - const QMap::ConstIterator end = d->headers->constEnd(); - while (it != end) { - ZipEntryP* entry = it.value(); - Q_ASSERT(entry != 0); - if ((entry->isEncrypted()) && d->skipAllEncrypted) { - ++it; - continue; - } - - bool skip = false; - ec = d->extractFile(it.key(), *entry, dir, options); - switch (ec) { - case Corrupted: - qDebug() << "Corrupted entry" << it.key(); - break; - case CreateDirFailed: - break; - case Skip: - skip = true; - break; - case SkipAll: - skip = true; - d->skipAllEncrypted = true; - break; - default: - ; - } - - if (ec != Ok && !skip) { - break; - } - - ++it; - } - - return ec; -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options) -{ - return extractFile(filename, QDir(dirname), options); -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return FileNotFound; - - QMap::Iterator itr = d->headers->find(filename); - if (itr != d->headers->end()) { - ZipEntryP* entry = itr.value(); - Q_ASSERT(entry != 0); - return d->extractFile(itr.key(), *entry, dir, options); - } - - return FileNotFound; -} - -/*! - Extracts a single file to a directory. -*/ -UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* outDev, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return FileNotFound; - if (!outDev) - return InvalidDevice; - - QMap::Iterator itr = d->headers->find(filename); - if (itr != d->headers->end()) { - ZipEntryP* entry = itr.value(); - Q_ASSERT(entry != 0); - return d->extractFile(itr.key(), *entry, outDev, options); - } - - return FileNotFound; -} - -/*! - Extracts a list of files. - Stops extraction at the first error (but continues if a file does not exist in the archive). - */ -UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return Ok; - - QDir dir(dirname); - ErrorCode ec; - - for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { - ec = extractFile(*itr, dir, options); - if (ec == FileNotFound) - continue; - if (ec != Ok) - return ec; - } - - return Ok; -} - -/*! - Extracts a list of files. - Stops extraction at the first error (but continues if a file does not exist in the archive). - */ -UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options) -{ - if (!d->device) - return NoOpenArchive; - if (!d->headers) - return Ok; - - ErrorCode ec; - - for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { - ec = extractFile(*itr, dir, options); - if (ec == FileNotFound) - continue; - if (ec != Ok) - return ec; - } - - return Ok; -} - -/*! - Remove/replace this method to add your own password retrieval routine. -*/ -void UnZip::setPassword(const QString& pwd) -{ - d->password = pwd; -} - -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: unzip.cpp +** Last updated [dd/mm/yyyy]: 08/07/2010 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "unzip.h" +#include "unzip_p.h" +#include "zipentry_p.h" + +#include +#include +#include +#include +#include + +// You can remove this #include if you replace the qDebug() statements. +#include + +/*! + \class UnZip unzip.h + + \brief PKZip 2.0 file decompression. + Compatibility with later versions is not ensured as they may use + unsupported compression algorithms. + Versions after 2.7 may have an incompatible header format and thus be + completely incompatible. +*/ + +/*! \enum UnZip::ErrorCode The result of a decompression operation. + \value UnZip::Ok No error occurred. + \value UnZip::ZlibInit Failed to init or load the zlib library. + \value UnZip::ZlibError The zlib library returned some error. + \value UnZip::OpenFailed Unable to create or open a device. + \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted. + \value UnZip::Corrupted Corrupted or invalid zip archive. + \value UnZip::WrongPassword Unable to decrypt a password protected file. + \value UnZip::NoOpenArchive No archive has been opened yet. + \value UnZip::FileNotFound Unable to find the requested file in the archive. + \value UnZip::ReadFailed Reading of a file failed. + \value UnZip::WriteFailed Writing of a file failed. + \value UnZip::SeekFailed Seek failed. + \value UnZip::CreateDirFailed Could not create a directory. + \value UnZip::InvalidDevice A null device has been passed as parameter. + \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive. + \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted. + + \value UnZip::Skip Internal use only. + \value UnZip::SkipAll Internal use only. +*/ + +/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods. + \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files. + \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory. + \value UnZip::VerifyOnly Doesn't actually extract files. + \value UnZip::NoSilentDirectoryCreation Doesn't attempt to silently create missing output directories. +*/ + +//! Local header size (excluding signature, excluding variable length fields) +#define UNZIP_LOCAL_HEADER_SIZE 26 +//! Central Directory file entry size (excluding signature, excluding variable length fields) +#define UNZIP_CD_ENTRY_SIZE_NS 42 +//! Data descriptor size (excluding signature) +#define UNZIP_DD_SIZE 12 +//! End Of Central Directory size (including signature, excluding variable length fields) +#define UNZIP_EOCD_SIZE 22 +//! Local header entry encryption header size +#define UNZIP_LOCAL_ENC_HEADER_SIZE 12 + +// Some offsets inside a CD record (excluding signature) +#define UNZIP_CD_OFF_VERSION_MADE 0 +#define UNZIP_CD_OFF_VERSION 2 +#define UNZIP_CD_OFF_GPFLAG 4 +#define UNZIP_CD_OFF_CMETHOD 6 +#define UNZIP_CD_OFF_MODT 8 +#define UNZIP_CD_OFF_MODD 10 +#define UNZIP_CD_OFF_CRC32 12 +#define UNZIP_CD_OFF_CSIZE 16 +#define UNZIP_CD_OFF_USIZE 20 +#define UNZIP_CD_OFF_NAMELEN 24 +#define UNZIP_CD_OFF_XLEN 26 +#define UNZIP_CD_OFF_COMMLEN 28 +#define UNZIP_CD_OFF_LHOFFSET 38 + +// Some offsets inside a local header record (excluding signature) +#define UNZIP_LH_OFF_VERSION 0 +#define UNZIP_LH_OFF_GPFLAG 2 +#define UNZIP_LH_OFF_CMETHOD 4 +#define UNZIP_LH_OFF_MODT 6 +#define UNZIP_LH_OFF_MODD 8 +#define UNZIP_LH_OFF_CRC32 10 +#define UNZIP_LH_OFF_CSIZE 14 +#define UNZIP_LH_OFF_USIZE 18 +#define UNZIP_LH_OFF_NAMELEN 22 +#define UNZIP_LH_OFF_XLEN 24 + +// Some offsets inside a data descriptor record (excluding signature) +#define UNZIP_DD_OFF_CRC32 0 +#define UNZIP_DD_OFF_CSIZE 4 +#define UNZIP_DD_OFF_USIZE 8 + +// Some offsets inside a EOCD record +#define UNZIP_EOCD_OFF_ENTRIES 6 +#define UNZIP_EOCD_OFF_CDOFF 12 +#define UNZIP_EOCD_OFF_COMMLEN 16 + +/*! + Max version handled by this API. + 0x14 = 2.0 --> full compatibility only up to this version; + later versions use unsupported features +*/ +#define UNZIP_VERSION 0x14 + +//! CRC32 routine +#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) + +OSDAB_BEGIN_NAMESPACE(Zip) + + +/************************************************************************ + ZipEntry +*************************************************************************/ + +/*! + ZipEntry constructor - initialize data. Type is set to File. +*/ +UnZip::ZipEntry::ZipEntry() +{ + compressedSize = uncompressedSize = crc32 = 0; + compression = NoCompression; + type = File; + encrypted = false; +} + + +/************************************************************************ + Private interface +*************************************************************************/ + +//! \internal +UnzipPrivate::UnzipPrivate() : + password(), + skipAllEncrypted(false), + headers(0), + device(0), + file(0), + uBuffer(0), + crcTable(0), + cdOffset(0), + eocdOffset(0), + cdEntryCount(0), + unsupportedEntryCount(0), + comment() +{ + uBuffer = (unsigned char*) buffer1; + crcTable = (quint32*) get_crc_table(); +} + +//! \internal +void UnzipPrivate::deviceDestroyed(QObject*) +{ + qDebug("Unexpected device destruction detected."); + do_closeArchive(); +} + +//! \internal Parses a Zip archive. +UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) +{ + Q_ASSERT(!device); + Q_ASSERT(dev); + + if (!(dev->isOpen() || dev->open(QIODevice::ReadOnly))) { + qDebug() << "Unable to open device for reading"; + return UnZip::OpenFailed; + } + + device = dev; + if (device != file) + connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); + + UnZip::ErrorCode ec; + + ec = seekToCentralDirectory(); + if (ec != UnZip::Ok) { + closeArchive(); + return ec; + } + + //! \todo Ignore CD entry count? CD may be corrupted. + if (cdEntryCount == 0) { + return UnZip::Ok; + } + + bool continueParsing = true; + + while (continueParsing) { + if (device->read(buffer1, 4) != 4) { + if (headers) { + qDebug() << "Corrupted zip archive. Some files might be extracted."; + ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted; + break; + } else { + closeArchive(); + qDebug() << "Corrupted or invalid zip archive. Closing."; + ec = UnZip::Corrupted; + break; + } + } + + if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) ) + break; + + if ((ec = parseCentralDirectoryRecord()) != UnZip::Ok) + break; + } + + if (ec != UnZip::Ok) + closeArchive(); + + return ec; +} + +/* + \internal Parses a local header record and makes some consistency check + with the information stored in the Central Directory record for this entry + that has been previously parsed. + \todo Optional consistency check (as a ExtractionOptions flag) + + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + + file name (variable size) + extra field (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry) +{ + Q_ASSERT(device); + + if (!device->seek(entry.lhOffset)) + return UnZip::SeekFailed; + + // Test signature + if (device->read(buffer1, 4) != 4) + return UnZip::ReadFailed; + + if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04)) + return UnZip::InvalidArchive; + + if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE) + return UnZip::ReadFailed; + + /* + Check 3rd general purpose bit flag. + + "bit 3: If this bit is set, the fields crc-32, compressed size + and uncompressed size are set to zero in the local + header. The correct values are put in the data descriptor + immediately following the compressed data." + */ + bool hasDataDescriptor = entry.hasDataDescriptor(); + + bool checkFailed = false; + + if (!checkFailed) + checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD); + if (!checkFailed) + checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG]; + if (!checkFailed) + checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1]; + if (!checkFailed) + checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT]; + if (!checkFailed) + checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1]; + if (!checkFailed) + checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD]; + if (!checkFailed) + checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1]; + if (!hasDataDescriptor) + { + if (!checkFailed) + checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32); + if (!checkFailed) + checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE); + if (!checkFailed) + checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE); + } + + if (checkFailed) + return UnZip::HeaderConsistencyError; + + // Check filename + quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN); + if (szName == 0) + return UnZip::HeaderConsistencyError; + + if (device->read(buffer2, szName) != szName) + return UnZip::ReadFailed; + + QString filename = QString::fromLatin1(buffer2, szName); + if (filename != path) { + qDebug() << "Filename in local header mismatches."; + return UnZip::HeaderConsistencyError; + } + + // Skip extra field + quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN); + if (szExtra != 0) { + if (!device->seek(device->pos() + szExtra)) + return UnZip::SeekFailed; + } + + entry.dataOffset = device->pos(); + + if (hasDataDescriptor) { + /* + The data descriptor has this OPTIONAL signature: PK\7\8 + We try to skip the compressed data relying on the size set in the + Central Directory record. + */ + if (!device->seek(device->pos() + entry.szComp)) + return UnZip::SeekFailed; + + // Read 4 bytes and check if there is a data descriptor signature + if (device->read(buffer2, 4) != 4) + return UnZip::ReadFailed; + + bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08; + if (hasSignature) { + if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE) + return UnZip::ReadFailed; + } else { + if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4) + return UnZip::ReadFailed; + } + + // DD: crc, compressed size, uncompressed size + if ( + entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) || + entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) || + entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE) + ) + return UnZip::HeaderConsistencyError; + } + + return UnZip::Ok; +} + +/*! \internal Attempts to find the start of the central directory record. + + We seek the file back until we reach the "End Of Central Directory" + signature PK\5\6. + + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in the + central directory on this disk 2 bytes + total number of entries in + the central directory 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + .ZIP file comment length 2 bytes + --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE --- + .ZIP file comment (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory() +{ + Q_ASSERT(device); + + qint64 length = device->size(); + qint64 offset = length - UNZIP_EOCD_SIZE; + + if (length < UNZIP_EOCD_SIZE) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + + if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) + return UnZip::ReadFailed; + + bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06); + + if (eocdFound) { + // Zip file has no comment (the only variable length field in the EOCD record) + eocdOffset = offset; + } else { + qint64 read; + char* p = 0; + + offset -= UNZIP_EOCD_SIZE; + + if (offset <= 0) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + + while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0) { + if ( (p = strstr(buffer1, "PK\5\6")) != 0) { + // Seek to the start of the EOCD record so we can read it fully + // Yes... we could simply read the missing bytes and append them to the buffer + // but this is far easier so heck it! + device->seek( offset + (p - buffer1) ); + eocdFound = true; + eocdOffset = offset + (p - buffer1); + + // Read EOCD record + if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE) + return UnZip::ReadFailed; + + break; + } + + // TODO: This is very slow and only a temporary bug fix. Need some pattern matching algorithm here. + offset -= 1 /*UNZIP_EOCD_SIZE*/; + if (offset <= 0) + return UnZip::InvalidArchive; + + if (!device->seek( offset )) + return UnZip::SeekFailed; + } + } + + if (!eocdFound) + return UnZip::InvalidArchive; + + // Parse EOCD to locate CD offset + offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4); + + cdOffset = offset; + + cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4); + + quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4); + if (commentLength != 0) { + QByteArray c = device->read(commentLength); + if (c.count() != commentLength) + return UnZip::ReadFailed; + + comment = c; + } + + // Seek to the start of the CD record + if (!device->seek( cdOffset )) + return UnZip::SeekFailed; + + return UnZip::Ok; +} + +/*! + \internal Parses a central directory record. + + Central Directory record structure: + + [file header 1] + . + . + . + [file header n] + [digital signature] // PKZip 6.2 or later only + + File header: + + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + file name (variable size) + extra field (variable size) + file comment (variable size) +*/ +UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord() +{ + Q_ASSERT(device); + + // Read CD record + if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS) + return UnZip::ReadFailed; + + bool skipEntry = false; + + // Get compression type so we can skip non compatible algorithms + quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD); + + // Get variable size fields length so we can skip the whole record + // if necessary + quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN); + quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN); + quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN); + + quint32 skipLength = szName + szExtra + szComment; + + UnZip::ErrorCode ec = UnZip::Ok; + + if ((compMethod != 0) && (compMethod != 8)) { + qDebug() << "Unsupported compression method. Skipping file."; + skipEntry = true; + } + + if (!skipEntry && szName == 0) { + qDebug() << "Skipping file with no name."; + skipEntry = true; + } + + QString filename; + if (device->read(buffer2, szName) != szName) { + ec = UnZip::ReadFailed; + skipEntry = true; + } else { + filename = QString::fromLatin1(buffer2, szName); + } + + // Unsupported features if version is bigger than UNZIP_VERSION + if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION) { + QString v = QString::number(buffer1[UNZIP_CD_OFF_VERSION]); + if (v.length() == 2) + v.insert(1, QLatin1Char('.')); + v = QString::fromLatin1("Unsupported PKZip version (%1). Skipping file: %2") + .arg(v, filename.isEmpty() ? QString::fromLatin1("") : filename); + qDebug() << v.toLatin1().constData(); + skipEntry = true; + } + + if (skipEntry) { + if (ec == UnZip::Ok) { + if (!device->seek( device->pos() + skipLength )) + ec = UnZip::SeekFailed; + unsupportedEntryCount++; + } + + return ec; + } + + ZipEntryP* h = new ZipEntryP; + h->compMethod = compMethod; + + h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG]; + h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1]; + + h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT]; + h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1]; + + h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD]; + h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1]; + + h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32); + h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE); + h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE); + + // Skip extra field (if any) + if (szExtra != 0) { + if (!device->seek( device->pos() + szExtra )) { + delete h; + return UnZip::SeekFailed; + } + } + + // Read comment field (if any) + if (szComment != 0) { + if (device->read(buffer2, szComment) != szComment) { + delete h; + return UnZip::ReadFailed; + } + + h->comment = QString::fromLatin1(buffer2, szComment); + } + + h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET); + + if (!headers) + headers = new QMap(); + headers->insert(filename, h); + + return UnZip::Ok; +} + +//! \internal Closes the archive and resets the internal status. +void UnzipPrivate::closeArchive() +{ + if (!device) { + Q_ASSERT(!file); + return; + } + + if (device != file) + disconnect(device, 0, this, 0); + + do_closeArchive(); +} + +//! \internal +void UnzipPrivate::do_closeArchive() +{ + skipAllEncrypted = false; + + if (headers) { + if (headers) + qDeleteAll(*headers); + delete headers; + headers = 0; + } + + device = 0; + + if (file) + delete file; + file = 0; + + cdOffset = eocdOffset = 0; + cdEntryCount = 0; + unsupportedEntryCount = 0; + + comment.clear(); +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, + const QDir& dir, UnZip::ExtractionOptions options) +{ + QString name(path); + QString dirname; + QString directory; + + const bool verify = (options & UnZip::VerifyOnly); + const int pos = name.lastIndexOf('/'); + + // This entry is for a directory + if (pos == name.length() - 1) { + if (verify) + return UnZip::Ok; + + if (options & UnZip::SkipPaths) + return UnZip::Ok; + + directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name)); + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create directory: %1").arg(directory); + return UnZip::CreateDirFailed; + } + + return UnZip::Ok; + } + + // Extract path from entry + if (verify) { + return extractFile(path, entry, 0, options); + } + + if (pos > 0) { + // get directory part + dirname = name.left(pos); + if (options & UnZip::SkipPaths) { + directory = dir.absolutePath(); + } else { + directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname)); + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create directory: %1").arg(directory); + return UnZip::CreateDirFailed; + } + } + name = name.right(name.length() - pos - 1); + } else { + directory = dir.absolutePath(); + } + + const bool silentDirectoryCreation = !(options & UnZip::NoSilentDirectoryCreation); + if (silentDirectoryCreation) { + if (!createDirectory(directory)) { + qDebug() << QString("Unable to create output directory %1").arg(directory); + return UnZip::CreateDirFailed; + } + } + + name = QString("%1/%2").arg(directory).arg(name); + + QFile outFile(name); + if (!outFile.open(QIODevice::WriteOnly)) { + qDebug() << QString("Unable to open %1 for writing").arg(name); + return UnZip::OpenFailed; + } + + UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options); + outFile.close(); + + const QDateTime lastModified = convertDateTime(entry.modDate, entry.modTime); + const bool setTimeOk = OSDAB_ZIP_MANGLE(setFileTimestamp)(name, lastModified); + if (!setTimeOk) { + qDebug() << QString("Unable to set last modified time on file: %1").arg(name); + } + + if (ec != UnZip::Ok) { + if (!outFile.remove()) + qDebug() << QString("Unable to remove corrupted file: %1").arg(name); + } + + return ec; +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::extractStoredFile( + const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, + UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + const bool isEncrypted = keys != 0; + + uInt rep = szComp / UNZIP_READ_BUFFER; + uInt rem = szComp % UNZIP_READ_BUFFER; + uInt cur = 0; + + // extract data + qint64 read; + quint64 tot = 0; + + while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 ) { + if (isEncrypted) + decryptBytes(*keys, buffer1, read); + + myCRC = crc32(myCRC, uBuffer, read); + if (!verify) { + if (outDev->write(buffer1, read) != read) + return UnZip::WriteFailed; + } + + cur++; + tot += read; + if (tot == szComp) + break; + } + + return (read < 0) + ? UnZip::ReadFailed + : UnZip::Ok; +} + +//! \internal +UnZip::ErrorCode UnzipPrivate::inflateFile( + const quint32 szComp, quint32** keys, quint32& myCRC, QIODevice* outDev, + UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + const bool isEncrypted = keys != 0; + Q_ASSERT(verify ? true : outDev != 0); + + uInt rep = szComp / UNZIP_READ_BUFFER; + uInt rem = szComp % UNZIP_READ_BUFFER; + uInt cur = 0; + + // extract data + qint64 read; + quint64 tot = 0; + + /* Allocate inflate state */ + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + + int zret; + + // Use inflateInit2 with negative windowBits to get raw decompression + if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK ) + return UnZip::ZlibError; + + int szDecomp; + + // Decompress until deflate stream ends or end of file + do { + read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem); + if (!read) + break; + + if (read < 0) { + (void)inflateEnd(&zstr); + return UnZip::ReadFailed; + } + + if (isEncrypted) + decryptBytes(*keys, buffer1, read); + + cur++; + tot += read; + + zstr.avail_in = (uInt) read; + zstr.next_in = (Bytef*) buffer1; + + // Run inflate() on input until output buffer not full + do { + zstr.avail_out = UNZIP_READ_BUFFER; + zstr.next_out = (Bytef*) buffer2;; + + zret = inflate(&zstr, Z_NO_FLUSH); + + switch (zret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&zstr); + return UnZip::WriteFailed; + default: + ; + } + + szDecomp = UNZIP_READ_BUFFER - zstr.avail_out; + if (!verify) { + if (outDev->write(buffer2, szDecomp) != szDecomp) { + inflateEnd(&zstr); + return UnZip::ZlibError; + } + } + + myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp); + + } while (zstr.avail_out == 0); + + } while (zret != Z_STREAM_END); + + inflateEnd(&zstr); + return UnZip::Ok; +} + +//! \internal \p outDev is null if the VerifyOnly option is set +UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, const ZipEntryP& entry, + QIODevice* outDev, UnZip::ExtractionOptions options) +{ + const bool verify = (options & UnZip::VerifyOnly); + + Q_UNUSED(options); + Q_ASSERT(device); + Q_ASSERT(verify ? true : outDev != 0); + + if (!entry.lhEntryChecked) { + UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry); + entry.lhEntryChecked = true; + if (ec != UnZip::Ok) + return ec; + } + + if (!device->seek(entry.dataOffset)) + return UnZip::SeekFailed; + + // Encryption keys + quint32 keys[3]; + quint32 szComp = entry.szComp; + if (entry.isEncrypted()) { + UnZip::ErrorCode e = testPassword(keys, path, entry); + if (e != UnZip::Ok) + { + qDebug() << QString("Unable to decrypt %1").arg(path); + return e; + }//! Encryption header size + szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size + } + + if (szComp == 0) { + if (entry.crc != 0) + return UnZip::Corrupted; + return UnZip::Ok; + } + + quint32 myCRC = crc32(0L, Z_NULL, 0); + quint32* k = keys; + + UnZip::ErrorCode ec = UnZip::Ok; + if (entry.compMethod == 0) { + ec = extractStoredFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); + } else if (entry.compMethod == 8) { + ec = inflateFile(szComp, entry.isEncrypted() ? &k : 0, myCRC, outDev, options); + } + + if (ec == UnZip::Ok && myCRC != entry.crc) + return UnZip::Corrupted; + + return UnZip::Ok; +} + +//! \internal Creates a new directory and all the needed parent directories. +bool UnzipPrivate::createDirectory(const QString& path) +{ + QDir d(path); + if (!d.exists() && !d.mkpath(path)) { + qDebug() << QString("Unable to create directory: %1").arg(path); + return false; + } + + return true; +} + +/*! + \internal Reads an quint32 (4 bytes) from a byte array starting at given offset. +*/ +quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const +{ + quint32 res = (quint32) data[offset]; + res |= (((quint32)data[offset+1]) << 8); + res |= (((quint32)data[offset+2]) << 16); + res |= (((quint32)data[offset+3]) << 24); + + return res; +} + +/*! + \internal Reads an quint64 (8 bytes) from a byte array starting at given offset. +*/ +quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const +{ + quint64 res = (quint64) data[offset]; + res |= (((quint64)data[offset+1]) << 8); + res |= (((quint64)data[offset+2]) << 16); + res |= (((quint64)data[offset+3]) << 24); + res |= (((quint64)data[offset+1]) << 32); + res |= (((quint64)data[offset+2]) << 40); + res |= (((quint64)data[offset+3]) << 48); + res |= (((quint64)data[offset+3]) << 56); + + return res; +} + +/*! + \internal Reads an quint16 (2 bytes) from a byte array starting at given offset. +*/ +quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const +{ + return (quint16) data[offset] | (((quint16)data[offset+1]) << 8); +} + +/*! + \internal Return the next byte in the pseudo-random sequence + */ +int UnzipPrivate::decryptByte(quint32 key2) const +{ + quint16 temp = ((quint16)(key2) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*! + \internal Update the encryption keys with the next byte of plain text + */ +void UnzipPrivate::updateKeys(quint32* keys, int c) const +{ + keys[0] = CRC32(keys[0], c); + keys[1] += keys[0] & 0xff; + keys[1] = keys[1] * 134775813L + 1; + keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24); +} + +/*! + \internal Initialize the encryption keys and the random header according to + the given password. + */ +void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const +{ + keys[0] = 305419896L; + keys[1] = 591751049L; + keys[2] = 878082192L; + + QByteArray pwdBytes = pwd.toLatin1(); + int sz = pwdBytes.size(); + const char* ascii = pwdBytes.data(); + + for (int i = 0; i < sz; ++i) + updateKeys(keys, (int)ascii[i]); +} + +/*! + \internal Attempts to test a password without actually extracting a file. + The \p file parameter can be used in the user interface or for debugging purposes + as it is the name of the encrypted file for wich the password is being tested. +*/ +UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header) +{ + Q_UNUSED(file); + Q_ASSERT(device); + + // read encryption keys + if (device->read(buffer1, 12) != 12) + return UnZip::Corrupted; + + // Replace this code if you want to i.e. call some dialog and ask the user for a password + initKeys(password, keys); + if (testKeys(header, keys)) + return UnZip::Ok; + + return UnZip::Skip; +} + +/*! + \internal Tests a set of keys on the encryption header. +*/ +bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys) +{ + char lastByte; + + // decrypt encryption header + for (int i = 0; i < 11; ++i) + updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2])); + updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2])); + + // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time + // with no extended header we have to check the crc high-order byte + char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24; + + return (lastByte == c); +} + +/*! + \internal Decrypts an array of bytes long \p read. +*/ +void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read) +{ + for (int i = 0; i < (int)read; ++i) + updateKeys(keys, buffer[i] ^= decryptByte(keys[2])); +} + +/*! + \internal Converts date and time values from ZIP format to a QDateTime object. +*/ +QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const +{ + QDateTime dt; + + // Usual PKZip low-byte to high-byte order + + // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day + quint16 year = (date[1] >> 1) & 127; + quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7); + quint16 day = date[0] & 31; + + // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision + quint16 hour = (time[1] >> 3) & 31; + quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7); + quint16 seconds = (time[0] & 31) * 2; + + dt.setDate(QDate(1980 + year, month, day)); + dt.setTime(QTime(hour, minutes, seconds)); + return dt; +} + + +/************************************************************************ + Public interface +*************************************************************************/ + +/*! + Creates a new Zip file decompressor. +*/ +UnZip::UnZip() : d(new UnzipPrivate) +{ +} + +/*! + Closes any open archive and releases used resources. +*/ +UnZip::~UnZip() +{ + closeArchive(); + delete d; +} + +/*! + Returns true if there is an open archive. +*/ +bool UnZip::isOpen() const +{ + return d->device; +} + +/*! + Opens a zip archive and reads the files list. Closes any previously opened archive. +*/ +UnZip::ErrorCode UnZip::openArchive(const QString& filename) +{ + closeArchive(); + + // closeArchive will destroy the file + d->file = new QFile(filename); + + if (!d->file->exists()) { + delete d->file; + d->file = 0; + return UnZip::FileNotFound; + } + + if (!d->file->open(QIODevice::ReadOnly)) { + delete d->file; + d->file = 0; + return UnZip::OpenFailed; + } + + return d->openArchive(d->file); +} + +/*! + Opens a zip archive and reads the entries list. + Closes any previously opened archive. + \warning The class takes DOES NOT take ownership of the device. +*/ +UnZip::ErrorCode UnZip::openArchive(QIODevice* device) +{ + closeArchive(); + + if (!device) { + qDebug() << "Invalid device."; + return UnZip::InvalidDevice; + } + + return d->openArchive(device); +} + +/*! + Closes the archive and releases all the used resources (like cached passwords). +*/ +void UnZip::closeArchive() +{ + d->closeArchive(); +} + +QString UnZip::archiveComment() const +{ + return d->comment; +} + +/*! + Returns a locale translated error string for a given error code. +*/ +QString UnZip::formatError(UnZip::ErrorCode c) const +{ + switch (c) + { + case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break; + case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break; + case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break; + case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break; + case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break; + case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break; + case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break; + case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break; + case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break; + case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break; + case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break; + case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break; + case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break; + case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break; + case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break; + case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break; + default: ; + } + + return QCoreApplication::translate("UnZip", "Unknown error."); +} + +/*! + Returns true if the archive contains a file with the given path and name. +*/ +bool UnZip::contains(const QString& file) const +{ + return d->headers ? d->headers->contains(file) : false; +} + +/*! + Returns complete paths of files and directories in this archive. +*/ +QStringList UnZip::fileList() const +{ + return d->headers ? d->headers->keys() : QStringList(); +} + +/*! + Returns information for each (correctly parsed) entry of this archive. +*/ +QList UnZip::entryList() const +{ + QList list; + if (!d->headers) + return list; + + for (QMap::ConstIterator it = d->headers->constBegin(); + it != d->headers->constEnd(); ++it) { + const ZipEntryP* entry = it.value(); + Q_ASSERT(entry != 0); + + ZipEntry z; + + z.filename = it.key(); + if (!entry->comment.isEmpty()) + z.comment = entry->comment; + z.compressedSize = entry->szComp; + z.uncompressedSize = entry->szUncomp; + z.crc32 = entry->crc; + z.lastModified = d->convertDateTime(entry->modDate, entry->modTime); + + z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression; + z.type = z.filename.endsWith("/") ? Directory : File; + + z.encrypted = entry->isEncrypted(); + + list.append(z); + } + + return list; +} + +/*! + Extracts the whole archive to a directory. +*/ +UnZip::ErrorCode UnZip::verifyArchive() +{ + return extractAll(QDir(), VerifyOnly); +} + +/*! + Extracts the whole archive to a directory. +*/ +UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options) +{ + return extractAll(QDir(dirname), options); +} + +/*! + Extracts the whole archive to a directory. + Stops extraction at the first error. +*/ +UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options) +{ + // this should only happen if we didn't call openArchive() yet + if (!d->device) + return NoOpenArchive; + + if (!d->headers) + return Ok; + + ErrorCode ec = Ok; + + QMap::ConstIterator it = d->headers->constBegin(); + const QMap::ConstIterator end = d->headers->constEnd(); + while (it != end) { + ZipEntryP* entry = it.value(); + Q_ASSERT(entry != 0); + if ((entry->isEncrypted()) && d->skipAllEncrypted) { + ++it; + continue; + } + + bool skip = false; + ec = d->extractFile(it.key(), *entry, dir, options); + switch (ec) { + case Corrupted: + qDebug() << "Corrupted entry" << it.key(); + break; + case CreateDirFailed: + break; + case Skip: + skip = true; + break; + case SkipAll: + skip = true; + d->skipAllEncrypted = true; + break; + default: + ; + } + + if (ec != Ok && !skip) { + break; + } + + ++it; + } + + return ec; +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options) +{ + return extractFile(filename, QDir(dirname), options); +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return FileNotFound; + + QMap::Iterator itr = d->headers->find(filename); + if (itr != d->headers->end()) { + ZipEntryP* entry = itr.value(); + Q_ASSERT(entry != 0); + return d->extractFile(itr.key(), *entry, dir, options); + } + + return FileNotFound; +} + +/*! + Extracts a single file to a directory. +*/ +UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* outDev, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return FileNotFound; + if (!outDev) + return InvalidDevice; + + QMap::Iterator itr = d->headers->find(filename); + if (itr != d->headers->end()) { + ZipEntryP* entry = itr.value(); + Q_ASSERT(entry != 0); + return d->extractFile(itr.key(), *entry, outDev, options); + } + + return FileNotFound; +} + +/*! + Extracts a list of files. + Stops extraction at the first error (but continues if a file does not exist in the archive). + */ +UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return Ok; + + QDir dir(dirname); + ErrorCode ec; + + for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { + ec = extractFile(*itr, dir, options); + if (ec == FileNotFound) + continue; + if (ec != Ok) + return ec; + } + + return Ok; +} + +/*! + Extracts a list of files. + Stops extraction at the first error (but continues if a file does not exist in the archive). + */ +UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options) +{ + if (!d->device) + return NoOpenArchive; + if (!d->headers) + return Ok; + + ErrorCode ec; + + for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr) { + ec = extractFile(*itr, dir, options); + if (ec == FileNotFound) + continue; + if (ec != Ok) + return ec; + } + + return Ok; +} + +/*! + Remove/replace this method to add your own password retrieval routine. +*/ +void UnZip::setPassword(const QString& pwd) +{ + d->password = pwd; +} + +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/unzip.h b/oracle/src/zip/unzip.h old mode 100644 new mode 100755 index ab57fdc36..e095a2287 --- a/oracle/src/zip/unzip.h +++ b/oracle/src/zip/unzip.h @@ -1,155 +1,152 @@ -/**************************************************************************** -** Filename: unzip.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_UNZIP__H -#define OSDAB_UNZIP__H - -#include "zipglobal.h" - -#include -#include -#include -#include - -class QDir; -class QFile; -class QIODevice; -class QString; - -OSDAB_BEGIN_NAMESPACE(Zip) - -class UnzipPrivate; - -class OSDAB_ZIP_EXPORT UnZip -{ -public: - enum ErrorCode - { - Ok, - ZlibInit, - ZlibError, - OpenFailed, - PartiallyCorrupted, - Corrupted, - WrongPassword, - NoOpenArchive, - FileNotFound, - ReadFailed, - WriteFailed, - SeekFailed, - CreateDirFailed, - InvalidDevice, - InvalidArchive, - HeaderConsistencyError, - - Skip, - SkipAll // internal use only - }; - - enum ExtractionOption - { - ExtractPaths = 0x0001, - SkipPaths = 0x0002, - VerifyOnly = 0x0004, - NoSilentDirectoryCreation = 0x0008 - }; - Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) - - enum CompressionMethod - { - NoCompression, - Deflated, - UnknownCompression - }; - - enum FileType - { - File, - Directory - }; - - struct ZipEntry - { - ZipEntry(); - - QString filename; - QString comment; - - quint32 compressedSize; - quint32 uncompressedSize; - quint32 crc32; - - QDateTime lastModified; - - CompressionMethod compression; - FileType type; - - bool encrypted; - }; - - UnZip(); - virtual ~UnZip(); - - bool isOpen() const; - - ErrorCode openArchive(const QString &filename); - ErrorCode openArchive(QIODevice *device); - void closeArchive(); - - QString archiveComment() const; - - QString formatError(UnZip::ErrorCode c) const; - - bool contains(const QString &file) const; - - QStringList fileList() const; - QList entryList() const; - - ErrorCode verifyArchive(); - - ErrorCode extractAll(const QString &dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractAll(const QDir &dir, ExtractionOptions options = ExtractPaths); - - ErrorCode extractFile(const QString &filename, const QString &dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString &filename, const QDir &dir, ExtractionOptions options = ExtractPaths); - ErrorCode extractFile(const QString &filename, QIODevice *device, ExtractionOptions options = ExtractPaths); - - ErrorCode - extractFiles(const QStringList &filenames, const QString &dirname, ExtractionOptions options = ExtractPaths); - ErrorCode extractFiles(const QStringList &filenames, const QDir &dir, ExtractionOptions options = ExtractPaths); - - void setPassword(const QString &pwd); - -private: - UnzipPrivate *d; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions) - -OSDAB_END_NAMESPACE - -#endif // OSDAB_UNZIP__H +/**************************************************************************** +** Filename: unzip.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_UNZIP__H +#define OSDAB_UNZIP__H + +#include "zipglobal.h" + +#include +#include +#include + +#include + +class QDir; +class QFile; +class QIODevice; +class QString; +class QStringList; + +OSDAB_BEGIN_NAMESPACE(Zip) + +class UnzipPrivate; + +class OSDAB_ZIP_EXPORT UnZip +{ +public: + enum ErrorCode + { + Ok, + ZlibInit, + ZlibError, + OpenFailed, + PartiallyCorrupted, + Corrupted, + WrongPassword, + NoOpenArchive, + FileNotFound, + ReadFailed, + WriteFailed, + SeekFailed, + CreateDirFailed, + InvalidDevice, + InvalidArchive, + HeaderConsistencyError, + + Skip, SkipAll // internal use only + }; + + enum ExtractionOption + { + ExtractPaths = 0x0001, + SkipPaths = 0x0002, + VerifyOnly = 0x0004, + NoSilentDirectoryCreation = 0x0008 + }; + Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption) + + enum CompressionMethod + { + NoCompression, Deflated, UnknownCompression + }; + + enum FileType + { + File, Directory + }; + + struct ZipEntry + { + ZipEntry(); + + QString filename; + QString comment; + + quint32 compressedSize; + quint32 uncompressedSize; + quint32 crc32; + + QDateTime lastModified; + + CompressionMethod compression; + FileType type; + + bool encrypted; + }; + + UnZip(); + virtual ~UnZip(); + + bool isOpen() const; + + ErrorCode openArchive(const QString& filename); + ErrorCode openArchive(QIODevice* device); + void closeArchive(); + + QString archiveComment() const; + + QString formatError(UnZip::ErrorCode c) const; + + bool contains(const QString& file) const; + + QStringList fileList() const; + QList entryList() const; + + ErrorCode verifyArchive(); + + ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths); + + ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths); + ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths); + + ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths); + ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths); + + void setPassword(const QString& pwd); + +private: + UnzipPrivate* d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions) + +OSDAB_END_NAMESPACE + +#endif // OSDAB_UNZIP__H diff --git a/oracle/src/zip/unzip_p.h b/oracle/src/zip/unzip_p.h index fca2b071d..21e9f2a2a 100755 --- a/oracle/src/zip/unzip_p.h +++ b/oracle/src/zip/unzip_p.h @@ -1,130 +1,130 @@ -/**************************************************************************** -** Filename: unzip_p.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 decompression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_UNZIP_P__H -#define OSDAB_UNZIP_P__H - -#include "unzip.h" -#include "zipentry_p.h" - -#include -#include - -// zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) -// we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) -#define UNZIP_READ_BUFFER (256*1024) - -OSDAB_BEGIN_NAMESPACE(Zip) - -class UnzipPrivate : public QObject -{ - Q_OBJECT - -public: - UnzipPrivate(); - - // Replace this with whatever else you use to store/retrieve the password. - QString password; - - bool skipAllEncrypted; - - QMap* headers; - - QIODevice* device; - QFile* file; - - char buffer1[UNZIP_READ_BUFFER]; - char buffer2[UNZIP_READ_BUFFER]; - - unsigned char* uBuffer; - const quint32* crcTable; - - // Central Directory (CD) offset - quint32 cdOffset; - // End of Central Directory (EOCD) offset - quint32 eocdOffset; - - // Number of entries in the Central Directory (as to the EOCD record) - quint16 cdEntryCount; - - // The number of detected entries that have been skipped because of a non compatible format - quint16 unsupportedEntryCount; - - QString comment; - - UnZip::ErrorCode openArchive(QIODevice* device); - - UnZip::ErrorCode seekToCentralDirectory(); - UnZip::ErrorCode parseCentralDirectoryRecord(); - UnZip::ErrorCode parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry); - - void closeArchive(); - - UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options); - UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, QIODevice* device, UnZip::ExtractionOptions options); - - UnZip::ErrorCode testPassword(quint32* keys, const QString&_file, const ZipEntryP& header); - bool testKeys(const ZipEntryP& header, quint32* keys); - - bool createDirectory(const QString& path); - - inline void decryptBytes(quint32* keys, char* buffer, qint64 read); - - inline quint32 getULong(const unsigned char* data, quint32 offset) const; - inline quint64 getULLong(const unsigned char* data, quint32 offset) const; - inline quint16 getUShort(const unsigned char* data, quint32 offset) const; - inline int decryptByte(quint32 key2) const; - inline void updateKeys(quint32* keys, int c) const; - inline void initKeys(const QString& pwd, quint32* keys) const; - - inline QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const; - -private slots: - void deviceDestroyed(QObject*); - -private: - UnZip::ErrorCode extractStoredFile(const quint32 szComp, quint32** keys, - quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); - UnZip::ErrorCode inflateFile(const quint32 szComp, quint32** keys, - quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); - void do_closeArchive(); -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_UNZIP_P__H +/**************************************************************************** +** Filename: unzip_p.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 decompression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_UNZIP_P__H +#define OSDAB_UNZIP_P__H + +#include "unzip.h" +#include "zipentry_p.h" + +#include +#include + +// zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) +// we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) +#define UNZIP_READ_BUFFER (256*1024) + +OSDAB_BEGIN_NAMESPACE(Zip) + +class UnzipPrivate : public QObject +{ + Q_OBJECT + +public: + UnzipPrivate(); + + // Replace this with whatever else you use to store/retrieve the password. + QString password; + + bool skipAllEncrypted; + + QMap* headers; + + QIODevice* device; + QFile* file; + + char buffer1[UNZIP_READ_BUFFER]; + char buffer2[UNZIP_READ_BUFFER]; + + unsigned char* uBuffer; + const quint32* crcTable; + + // Central Directory (CD) offset + quint32 cdOffset; + // End of Central Directory (EOCD) offset + quint32 eocdOffset; + + // Number of entries in the Central Directory (as to the EOCD record) + quint16 cdEntryCount; + + // The number of detected entries that have been skipped because of a non compatible format + quint16 unsupportedEntryCount; + + QString comment; + + UnZip::ErrorCode openArchive(QIODevice* device); + + UnZip::ErrorCode seekToCentralDirectory(); + UnZip::ErrorCode parseCentralDirectoryRecord(); + UnZip::ErrorCode parseLocalHeaderRecord(const QString& path, const ZipEntryP& entry); + + void closeArchive(); + + UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options); + UnZip::ErrorCode extractFile(const QString& path, const ZipEntryP& entry, QIODevice* device, UnZip::ExtractionOptions options); + + UnZip::ErrorCode testPassword(quint32* keys, const QString& file, const ZipEntryP& header); + bool testKeys(const ZipEntryP& header, quint32* keys); + + bool createDirectory(const QString& path); + + inline void decryptBytes(quint32* keys, char* buffer, qint64 read); + + inline quint32 getULong(const unsigned char* data, quint32 offset) const; + inline quint64 getULLong(const unsigned char* data, quint32 offset) const; + inline quint16 getUShort(const unsigned char* data, quint32 offset) const; + inline int decryptByte(quint32 key2) const; + inline void updateKeys(quint32* keys, int c) const; + inline void initKeys(const QString& pwd, quint32* keys) const; + + inline QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const; + +private slots: + void deviceDestroyed(QObject*); + +private: + UnZip::ErrorCode extractStoredFile(const quint32 szComp, quint32** keys, + quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); + UnZip::ErrorCode inflateFile(const quint32 szComp, quint32** keys, + quint32& myCRC, QIODevice* outDev, UnZip::ExtractionOptions options); + void do_closeArchive(); +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_UNZIP_P__H diff --git a/oracle/src/zip/zip.cpp b/oracle/src/zip/zip.cpp index 5b0177293..aa33bfc70 100755 --- a/oracle/src/zip/zip.cpp +++ b/oracle/src/zip/zip.cpp @@ -1,1619 +1,1619 @@ -/**************************************************************************** -** Filename: zip.cpp -** Last updated [dd/mm/yyyy]: 01/02/2007 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "zip.h" -#include "zip_p.h" -#include "zipentry_p.h" - -// we only use this to seed the random number generator -#include - -#include -#include -#include -#include -#include -#include -#include - -// You can remove this #include if you replace the qDebug() statements. -#include - - -/*! #define OSDAB_ZIP_NO_PNG_RLE to disable the use of Z_RLE compression strategy with - PNG files (achieves slightly better compression levels according to the authors). -*/ -// #define OSDAB_ZIP_NO_PNG_RLE - -#define OSDAB_ZIP_NO_DEBUG - -//! Local header size (including signature, excluding variable length fields) -#define ZIP_LOCAL_HEADER_SIZE 30 -//! Encryption header size -#define ZIP_LOCAL_ENC_HEADER_SIZE 12 -//! Data descriptor size (signature included) -#define ZIP_DD_SIZE_WS 16 -//! Central Directory record size (signature included) -#define ZIP_CD_SIZE 46 -//! End of Central Directory record size (signature included) -#define ZIP_EOCD_SIZE 22 - -// Some offsets inside a local header record (signature included) -#define ZIP_LH_OFF_VERS 4 -#define ZIP_LH_OFF_GPFLAG 6 -#define ZIP_LH_OFF_CMET 8 -#define ZIP_LH_OFF_MODT 10 -#define ZIP_LH_OFF_MODD 12 -#define ZIP_LH_OFF_CRC 14 -#define ZIP_LH_OFF_CSIZE 18 -#define ZIP_LH_OFF_USIZE 22 -#define ZIP_LH_OFF_NAMELEN 26 -#define ZIP_LH_OFF_XLEN 28 - -// Some offsets inside a data descriptor record (including signature) -#define ZIP_DD_OFF_CRC32 4 -#define ZIP_DD_OFF_CSIZE 8 -#define ZIP_DD_OFF_USIZE 12 - -// Some offsets inside a Central Directory record (including signature) -#define ZIP_CD_OFF_MADEBY 4 -#define ZIP_CD_OFF_VERSION 6 -#define ZIP_CD_OFF_GPFLAG 8 -#define ZIP_CD_OFF_CMET 10 -#define ZIP_CD_OFF_MODT 12 -#define ZIP_CD_OFF_MODD 14 -#define ZIP_CD_OFF_CRC 16 -#define ZIP_CD_OFF_CSIZE 20 -#define ZIP_CD_OFF_USIZE 24 -#define ZIP_CD_OFF_NAMELEN 28 -#define ZIP_CD_OFF_XLEN 30 -#define ZIP_CD_OFF_COMMLEN 32 -#define ZIP_CD_OFF_DISKSTART 34 -#define ZIP_CD_OFF_IATTR 36 -#define ZIP_CD_OFF_EATTR 38 -#define ZIP_CD_OFF_LHOFF 42 - -// Some offsets inside a EOCD record (including signature) -#define ZIP_EOCD_OFF_DISKNUM 4 -#define ZIP_EOCD_OFF_CDDISKNUM 6 -#define ZIP_EOCD_OFF_ENTRIES 8 -#define ZIP_EOCD_OFF_CDENTRIES 10 -#define ZIP_EOCD_OFF_CDSIZE 12 -#define ZIP_EOCD_OFF_CDOFF 16 -#define ZIP_EOCD_OFF_COMMLEN 20 - -//! PKZip version for archives created by this API -#define ZIP_VERSION 0x14 - -//! Do not store very small files as the compression headers overhead would be to big -#define ZIP_COMPRESSION_THRESHOLD 60 - -/*! - \class Zip zip.h - - \brief Zip file compression. - - Some quick usage examples. - - \verbatim - Suppose you have this directory structure: - - /home/user/dir1/file1.1 - /home/user/dir1/file1.2 - /home/user/dir1/dir1.1/ - /home/user/dir1/dir1.2/file1.2.1 - - EXAMPLE 1: - myZipInstance.addDirectory("/home/user/dir1"); - - RESULT: - Beheaves like any common zip software and creates a zip file with this structure: - - dir1/file1.1 - dir1/file1.2 - dir1/dir1.1/ - dir1/dir1.2/file1.2.1 - - EXAMPLE 2: - myZipInstance.addDirectory("/home/user/dir1", "myRoot/myFolder"); - - RESULT: - Adds a custom root to the paths and creates a zip file with this structure: - - myRoot/myFolder/dir1/file1.1 - myRoot/myFolder/dir1/file1.2 - myRoot/myFolder/dir1/dir1.1/ - myRoot/myFolder/dir1/dir1.2/file1.2.1 - - EXAMPLE 3: - myZipInstance.addDirectory("/home/user/dir1", Zip::AbsolutePaths); - - NOTE: - Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH). - - RESULT: - Preserves absolute paths and creates a zip file with this structure: - - /home/user/dir1/file1.1 - /home/user/dir1/file1.2 - /home/user/dir1/dir1.1/ - /home/user/dir1/dir1.2/file1.2.1 - - EXAMPLE 4: - myZipInstance.setPassword("hellopass"); - myZipInstance.addDirectory("/home/user/dir1", "/"); - - RESULT: - Adds and encrypts the files in /home/user/dir1, creating the following zip structure: - - /dir1/file1.1 - /dir1/file1.2 - /dir1/dir1.1/ - /dir1/dir1.2/file1.2.1 - - EXAMPLE 5: - myZipInstance.addDirectory("/home/user/dir1", Zip::IgnoreRoot); - - RESULT: - Adds the files in /home/user/dir1 but doesn't create the top level - directory: - - file1.1 - file1.2 - dir1.1/ - dir1.2/file1.2.1 - - EXAMPLE 5: - myZipInstance.addDirectory("/home/user/dir1", "data/backup", Zip::IgnoreRoot); - - RESULT: - Adds the files in /home/user/dir1 but uses "data/backup" as top level - directory instead of "dir1": - - data/backup/file1.1 - data/backup/file1.2 - data/backup/dir1.1/ - data/backup/dir1.2/file1.2.1 - - \endverbatim -*/ - -/*! \enum Zip::ErrorCode The result of a compression operation. - \value Zip::Ok No error occurred. - \value Zip::ZlibInit Failed to init or load the zlib library. - \value Zip::ZlibError The zlib library returned some error. - \value Zip::FileExists The file already exists and will not be overwritten. - \value Zip::OpenFailed Unable to create or open a device. - \value Zip::NoOpenArchive CreateArchive() has not been called yet. - \value Zip::FileNotFound File or directory does not exist. - \value Zip::ReadFailed Reading of a file failed. - \value Zip::WriteFailed Writing of a file failed. - \value Zip::SeekFailed Seek failed. -*/ - -/*! \enum Zip::CompressionLevel Returns the result of a decompression operation. - \value Zip::Store No compression. - \value Zip::Deflate1 Deflate compression level 1(lowest compression). - \value Zip::Deflate1 Deflate compression level 2. - \value Zip::Deflate1 Deflate compression level 3. - \value Zip::Deflate1 Deflate compression level 4. - \value Zip::Deflate1 Deflate compression level 5. - \value Zip::Deflate1 Deflate compression level 6. - \value Zip::Deflate1 Deflate compression level 7. - \value Zip::Deflate1 Deflate compression level 8. - \value Zip::Deflate1 Deflate compression level 9 (maximum compression). - \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression). - \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed. - \value Zip::AutoFull Use both CPU and MIME type detection. -*/ - -namespace { - -struct ZippedDir { - bool init; - QString actualRoot; - int files; - ZippedDir() : init(false), actualRoot(), files(0) {} -}; - -void checkRootPath(QString& path) -{ - const bool isUnixRoot = path.length() == 1 && path.at(0) == QLatin1Char('/'); - if (!path.isEmpty() && !isUnixRoot) { - while (path.endsWith(QLatin1String("\\"))) - path.truncate(path.length() - 1); - - int sepCount = 0; - for (int i = path.length()-1; i >= 0; --i) { - if (path.at(i) == QLatin1Char('/')) - ++sepCount; - else break; - } - - if (sepCount > 1) - path.truncate(path.length() - (sepCount-1)); - else if (sepCount == 0) - path.append(QLatin1String("/")); - } -} - -} - -////////////////////////////////////////////////////////////////////////// - -OSDAB_BEGIN_NAMESPACE(Zip) - -/************************************************************************ - Private interface -*************************************************************************/ - -//! \internal -ZipPrivate::ZipPrivate() : - headers(0), - device(0), - file(0), - uBuffer(0), - crcTable(0), - comment(), - password() -{ - // keep an unsigned pointer so we avoid to over bloat the code with casts - uBuffer = (unsigned char*) buffer1; - crcTable = get_crc_table(); -} - -//! \internal -ZipPrivate::~ZipPrivate() -{ - closeArchive(); -} - -//! \internal -Zip::ErrorCode ZipPrivate::createArchive(QIODevice* dev) -{ - Q_ASSERT(dev); - - if (device) - closeArchive(); - - device = dev; - if (device != file) - connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); - - if (!device->isOpen()) { - if (!device->open(QIODevice::ReadOnly)) { - delete device; - device = 0; - qDebug() << "Unable to open device for writing."; - return Zip::OpenFailed; - } - } - - headers = new QMap; - return Zip::Ok; -} - -//! \internal -void ZipPrivate::deviceDestroyed(QObject*) -{ - qDebug("Unexpected device destruction detected."); - do_closeArchive(); -} - -/*! Returns true if an entry for \p info has already been added. - Uses file size and lower case absolute path to compare entries. -*/ -bool ZipPrivate::containsEntry(const QFileInfo& info) const -{ - if (!headers || headers->isEmpty()) - return false; - - const qint64 sz = info.size(); - const QString path = info.absoluteFilePath().toLower(); - - QMap::ConstIterator b = headers->constBegin(); - const QMap::ConstIterator e = headers->constEnd(); - while (b != e) { - const ZipEntryP* e = b.value(); - if (e->fileSize == sz && e->absolutePath == path) - return true; - ++b; - } - - return false; -} - -//! \internal Actual implementation of the addDirectory* methods. -Zip::ErrorCode ZipPrivate::addDirectory(const QString& path, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, int hierarchyLevel, - int* addedFiles) -{ - if (addedFiles) - ++(*addedFiles); - - // Bad boy didn't call createArchive() yet :) - if (!device) - return Zip::NoOpenArchive; - - QDir dir(path); - if (!dir.exists()) - return Zip::FileNotFound; - - // Remove any trailing separator - QString actualRoot = root.trimmed(); - - // Preserve Unix root but make sure the path ends only with a single - // unix like separator - ::checkRootPath(actualRoot); - - // QDir::cleanPath() fixes some issues with QDir::dirName() - QFileInfo current(QDir::cleanPath(path)); - - const bool path_absolute = options.testFlag(Zip::AbsolutePaths); - const bool path_ignore = options.testFlag(Zip::IgnorePaths); - const bool path_noroot = options.testFlag(Zip::IgnoreRoot); - - if (path_absolute && !path_ignore && !path_noroot) { - QString absolutePath = extractRoot(path, options); - if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) - absolutePath.append(QLatin1String("/")); - actualRoot.append(absolutePath); - } - - const bool skipDirName = !hierarchyLevel && path_noroot; - if (!path_ignore && !skipDirName) { - actualRoot.append(QDir(current.absoluteFilePath()).dirName()); - actualRoot.append(QLatin1String("/")); - } - - // actualRoot now contains the path of the file relative to the zip archive - // with a trailing / - - const bool skipBad = options & Zip::SkipBadFiles; - const bool noDups = options & Zip::CheckForDuplicates; - - const QDir::Filters dir_filter = - QDir::Files | - QDir::Dirs | - QDir::NoDotAndDotDot | - QDir::NoSymLinks; - const QDir::SortFlags dir_sort = - QDir::DirsFirst; - QFileInfoList list = dir.entryInfoList(dir_filter, dir_sort); - - Zip::ErrorCode ec = Zip::Ok; - bool filesAdded = false; - - Zip::CompressionOptions recursionOptions; - if (path_ignore) - recursionOptions |= Zip::IgnorePaths; - else recursionOptions |= Zip::RelativePaths; - - for (int i = 0; i < list.size(); ++i) { - QFileInfo info = list.at(i); - const QString absPath = info.absoluteFilePath(); - if (noDups && containsEntry(info)) - continue; - if (info.isDir()) { - // Recursion - ec = addDirectory(absPath, actualRoot, recursionOptions, - level, hierarchyLevel + 1, addedFiles); - } else { - ec = createEntry(info, actualRoot, level); - if (ec == Zip::Ok) { - filesAdded = true; - if (addedFiles) - ++(*addedFiles); - } - } - - if (ec != Zip::Ok && !skipBad) { - break; - } - } - - // We need an explicit record for this dir - // Non-empty directories don't need it because they have a path component in the filename - if (!filesAdded && !path_ignore) - ec = createEntry(current, actualRoot, level); - - return ec; -} - -//! \internal Actual implementation of the addFile methods. -Zip::ErrorCode ZipPrivate::addFiles(const QStringList& files, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int* addedFiles) -{ - if (addedFiles) - *addedFiles = 0; - - const bool skipBad = options & Zip::SkipBadFiles; - const bool noDups = options & Zip::CheckForDuplicates; - - // Bad boy didn't call createArchive() yet :) - if (!device) - return Zip::NoOpenArchive; - - QFileInfoList paths; - paths.reserve(files.size()); - for (int i = 0; i < files.size(); ++i) { - QFileInfo info(files.at(i)); - if (noDups && (paths.contains(info) || containsEntry(info))) - continue; - if (!info.exists() || !info.isReadable()) { - if (skipBad) { - continue; - } else { - return Zip::FileNotFound; - } - } - paths.append(info); - } - - if (paths.isEmpty()) - return Zip::Ok; - - // Remove any trailing separator - QString actualRoot = root.trimmed(); - - // Preserve Unix root but make sure the path ends only with a single - // unix like separator - ::checkRootPath(actualRoot); - - const bool path_absolute = options.testFlag(Zip::AbsolutePaths); - const bool path_ignore = options.testFlag(Zip::IgnorePaths); - const bool path_noroot = options.testFlag(Zip::IgnoreRoot); - - Zip::ErrorCode ec = Zip::Ok; - QHash dirMap; - - for (int i = 0; i < paths.size(); ++i) { - const QFileInfo& info = paths.at(i); - const QString path = QFileInfo(QDir::cleanPath(info.absolutePath())).absolutePath(); - - ZippedDir& zd = dirMap[path]; - if (!zd.init) { - zd.init = true; - zd.actualRoot = actualRoot; - if (path_absolute && !path_ignore && !path_noroot) { - QString absolutePath = extractRoot(path, options); - if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) - absolutePath.append(QLatin1String("/")); - zd.actualRoot.append(absolutePath); - } - - if (!path_ignore && !path_noroot) { - zd.actualRoot.append(QDir(path).dirName()); - zd.actualRoot.append(QLatin1String("/")); - } - } - - // zd.actualRoot now contains the path of the file relative to the zip archive - // with a trailing / - - if (info.isDir()) { - // Recursion - ec = addDirectory(info.absoluteFilePath(), actualRoot, options, - level, 1, addedFiles); - } else { - ec = createEntry(info, actualRoot, level); - if (ec == Zip::Ok) { - ++zd.files; - if (addedFiles) - ++(*addedFiles); - } - } - - if (ec != Zip::Ok && !skipBad) { - break; - } - } - - // Create explicit records for empty directories - if (!path_ignore) { - QHash::ConstIterator b = dirMap.constBegin(); - const QHash::ConstIterator e = dirMap.constEnd(); - while (b != e) { - const ZippedDir& zd = b.value(); - if (zd.files <= 0) { - ec = createEntry(b.key(), zd.actualRoot, level); - } - ++b; - } - } - - return ec; -} - -//! \internal \p file must be a file and not a directory. -Zip::ErrorCode ZipPrivate::deflateFile(const QFileInfo& fileInfo, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys) -{ - const QString path = fileInfo.absoluteFilePath(); - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) { - qDebug() << QString("An error occurred while opening %1").arg(path); - return Zip::OpenFailed; - } - - const Zip::ErrorCode ec = (level == Zip::Store) - ? storeFile(path, file, crc, written, keys) - : compressFile(path, file, crc, written, level, keys); - - file.close(); - return ec; -} - -//! \internal -Zip::ErrorCode ZipPrivate::storeFile(const QString& path, QIODevice& file, - quint32& crc, qint64& totalWritten, quint32** keys) -{ - Q_UNUSED(path); - - qint64 read = 0; - qint64 written = 0; - - const bool encrypt = keys != 0; - - totalWritten = 0; - crc = crc32(0L, Z_NULL, 0); - - while ( (read = file.read(buffer1, ZIP_READ_BUFFER)) > 0 ) { - crc = crc32(crc, uBuffer, read); - if (encrypt) - encryptBytes(*keys, buffer1, read); - written = device->write(buffer1, read); - totalWritten += written; - if (written != read) { - return Zip::WriteFailed; - } - } - - return Zip::Ok; -} - -//! \internal -int ZipPrivate::compressionStrategy(const QString& path, QIODevice& file) const -{ - Q_UNUSED(file); - -#ifndef OSDAB_ZIP_NO_PNG_RLE - return Z_DEFAULT_STRATEGY; -#endif - const bool isPng = path.endsWith(QLatin1String("png"), Qt::CaseInsensitive); - return isPng ? Z_RLE : Z_DEFAULT_STRATEGY; -} - -//! \internal -Zip::ErrorCode ZipPrivate::compressFile(const QString& path, QIODevice& file, - quint32& crc, qint64& totalWritten, const Zip::CompressionLevel& level, quint32** keys) -{ - qint64 read = 0; - qint64 written = 0; - - qint64 totRead = 0; - qint64 toRead = file.size(); - - const bool encrypt = keys != 0; - const int strategy = compressionStrategy(path, file); - - totalWritten = 0; - crc = crc32(0L, Z_NULL, 0); - - z_stream zstr; - - // Initialize zalloc, zfree and opaque before calling the init function - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - - int zret; - - // Use deflateInit2 with negative windowBits to get raw compression - if ((zret = deflateInit2_( - &zstr, - (int)level, // compression level - Z_DEFLATED, // method - -MAX_WBITS, // windowBits - 8, // memLevel - strategy, - ZLIB_VERSION, - sizeof(z_stream) - )) != Z_OK ) { - qDebug() << "Could not initialize zlib for compression"; - return Zip::ZlibError; - } - - qint64 compressed; - int flush = Z_NO_FLUSH; - do { - read = file.read(buffer1, ZIP_READ_BUFFER); - totRead += read; - if (!read) - break; - - if (read < 0) { - deflateEnd(&zstr); - qDebug() << QString("Error while reading %1").arg(path); - return Zip::ReadFailed; - } - - crc = crc32(crc, uBuffer, read); - - zstr.next_in = (Bytef*) buffer1; - zstr.avail_in = (uInt)read; - - // Tell zlib if this is the last chunk we want to encode - // by setting the flush parameter to Z_FINISH - flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH; - - // Run deflate() on input until output buffer not full - // finish compression if all of source has been read in - do { - zstr.next_out = (Bytef*) buffer2; - zstr.avail_out = ZIP_READ_BUFFER; - - zret = deflate(&zstr, flush); - // State not clobbered - Q_ASSERT(zret != Z_STREAM_ERROR); - - // Write compressed data to file and empty buffer - compressed = ZIP_READ_BUFFER - zstr.avail_out; - - if (encrypt) - encryptBytes(*keys, buffer2, compressed); - - written = device->write(buffer2, compressed); - totalWritten += written; - - if (written != compressed) { - deflateEnd(&zstr); - qDebug() << QString("Error while writing %1").arg(path); - return Zip::WriteFailed; - } - - } while (zstr.avail_out == 0); - - // All input will be used - Q_ASSERT(zstr.avail_in == 0); - - } while (flush != Z_FINISH); - - // Stream will be complete - Q_ASSERT(zret == Z_STREAM_END); - deflateEnd(&zstr); - - return Zip::Ok; -} - -//! \internal Writes a new entry in the zip file. -Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, - Zip::CompressionLevel level) -{ - const bool dirOnly = file.isDir(); - - // entryName contains the path as it should be written - // in the zip file records - const QString entryName = dirOnly - ? root - : root + file.fileName(); - - // Directory entry - if (dirOnly || file.size() < ZIP_COMPRESSION_THRESHOLD) { - level = Zip::Store; - } else { - switch (level) { - case Zip::AutoCPU: - level = Zip::Deflate5; -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - case Zip::AutoMIME: - level = detectCompressionByMime(file.completeSuffix().toLower()); -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - case Zip::AutoFull: - level = detectCompressionByMime(file.completeSuffix().toLower()); -#ifndef OSDAB_ZIP_NO_DEBUG - qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); -#endif - break; - default: ; - } - } - - - - // create header and store it to write a central directory later - QScopedPointer h(new ZipEntryP); - h->absolutePath = file.absoluteFilePath().toLower(); - h->fileSize = file.size(); - - // Set encryption bit and set the data descriptor bit - // so we can use mod time instead of crc for password check - bool encrypt = !dirOnly && !password.isEmpty(); - if (encrypt) - h->gpFlag[0] |= 9; - - QDateTime dt = file.lastModified(); - dt = OSDAB_ZIP_MANGLE(fromFileTimestamp)(dt); - QDate d = dt.date(); - h->modDate[1] = ((d.year() - 1980) << 1) & 254; - h->modDate[1] |= ((d.month() >> 3) & 1); - h->modDate[0] = ((d.month() & 7) << 5) & 224; - h->modDate[0] |= d.day(); - - QTime t = dt.time(); - h->modTime[1] = (t.hour() << 3) & 248; - h->modTime[1] |= ((t.minute() >> 3) & 7); - h->modTime[0] = ((t.minute() & 7) << 5) & 224; - h->modTime[0] |= t.second() / 2; - - h->szUncomp = dirOnly ? 0 : file.size(); - - h->compMethod = (level == Zip::Store) ? 0 : 0x0008; - - // **** Write local file header **** - - // signature - buffer1[0] = 'P'; buffer1[1] = 'K'; - buffer1[2] = 0x3; buffer1[3] = 0x4; - - // version needed to extract - buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION; - buffer1[ZIP_LH_OFF_VERS + 1] = 0; - - // general purpose flag - buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0]; - buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1]; - - // compression method - buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF; - buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF; - - // last mod file time - buffer1[ZIP_LH_OFF_MODT] = h->modTime[0]; - buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1]; - - // last mod file date - buffer1[ZIP_LH_OFF_MODD] = h->modDate[0]; - buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1]; - - // skip crc (4bytes) [14,15,16,17] - - // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21]) - buffer1[ZIP_LH_OFF_CSIZE] = - buffer1[ZIP_LH_OFF_CSIZE + 1] = - buffer1[ZIP_LH_OFF_CSIZE + 2] = - buffer1[ZIP_LH_OFF_CSIZE + 3] = 0; - - h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0; - - // uncompressed size [22,23,24,25] - setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE); - - // filename length - QByteArray entryNameBytes = entryName.toLatin1(); - int sz = entryNameBytes.size(); - - buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF; - buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; - - // extra field length - buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0; - - // Store offset to write crc and compressed size - h->lhOffset = device->pos(); - quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC; - - if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE) { - return Zip::WriteFailed; - } - - // Write out filename - if (device->write(entryNameBytes) != sz) { - return Zip::WriteFailed; - } - - // Encryption keys - quint32 keys[3] = { 0, 0, 0 }; - - if (encrypt) { - // **** encryption header **** - - // XOR with PI to ensure better random numbers - // with poorly implemented rand() as suggested by Info-Zip - srand(time(NULL) ^ 3141592654UL); - int randByte; - - initKeys(keys); - for (int i = 0; i < 10; ++i) { - randByte = (rand() >> 7) & 0xff; - buffer1[i] = decryptByte(keys[2]) ^ randByte; - updateKeys(keys, randByte); - } - - // Encrypt encryption header - initKeys(keys); - for (int i = 0; i < 10; ++i) { - randByte = decryptByte(keys[2]); - updateKeys(keys, buffer1[i]); - buffer1[i] ^= randByte; - } - - // We don't know the CRC at this time, so we use the modification time - // as the last two bytes - randByte = decryptByte(keys[2]); - updateKeys(keys, h->modTime[0]); - buffer1[10] ^= randByte; - - randByte = decryptByte(keys[2]); - updateKeys(keys, h->modTime[1]); - buffer1[11] ^= randByte; - - // Write out encryption header - if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE) { - return Zip::WriteFailed; - } - } - - quint32 crc = 0; - qint64 written = 0; - - if (!dirOnly) { - quint32* k = keys; - const Zip::ErrorCode ec = deflateFile(file, crc, written, level, encrypt ? &k : 0); - if (ec != Zip::Ok) - return ec; - Q_ASSERT(!h.isNull()); - } - - // Store end of entry offset - quint32 current = device->pos(); - - // Update crc and compressed size in local header - if (!device->seek(crcOffset)) { - return Zip::SeekFailed; - } - - h->crc = dirOnly ? 0 : crc; - h->szComp += written; - - setULong(h->crc, buffer1, 0); - setULong(h->szComp, buffer1, 4); - if ( device->write(buffer1, 8) != 8) { - return Zip::WriteFailed; - } - - // Seek to end of entry - if (!device->seek(current)) { - return Zip::SeekFailed; - } - - if ((h->gpFlag[0] & 8) == 8) { - // Write data descriptor - - // Signature: PK\7\8 - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x07; - buffer1[3] = 0x08; - - // CRC - setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32); - - // Compressed size - setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE); - - // Uncompressed size - setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE); - - if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS) { - return Zip::WriteFailed; - } - } - - headers->insert(entryName, h.take()); - return Zip::Ok; -} - -//! \internal -int ZipPrivate::decryptByte(quint32 key2) const -{ - quint16 temp = ((quint16)(key2) & 0xffff) | 2; - return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -} - -//! \internal Writes an quint32 (4 bytes) to a byte array at given offset. -void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset) -{ - buffer[offset+3] = ((v >> 24) & 0xFF); - buffer[offset+2] = ((v >> 16) & 0xFF); - buffer[offset+1] = ((v >> 8) & 0xFF); - buffer[offset] = (v & 0xFF); -} - -//! \internal Initializes decryption keys using a password. -void ZipPrivate::initKeys(quint32* keys) const -{ - // Encryption keys initialization constants are taken from the - // PKZip file format specification docs - keys[0] = 305419896L; - keys[1] = 591751049L; - keys[2] = 878082192L; - - QByteArray pwdBytes = password.toLatin1(); - int sz = pwdBytes.size(); - const char* ascii = pwdBytes.data(); - - for (int i = 0; i < sz; ++i) - updateKeys(keys, (int)ascii[i]); -} - -//! Updates a one-char-only CRC; it's the Info-Zip macro re-adapted. -quint32 ZipPrivate::updateChecksum(const quint32& crc, const quint32& val) const -{ - return quint32(crcTable[quint32(crc^val) & 0xff] ^ crc_t(crc >> 8)); -} - -//! \internal Updates encryption keys. -void ZipPrivate::updateKeys(quint32* keys, int c) const -{ - keys[0] = updateChecksum(keys[0], c); - keys[1] += keys[0] & 0xff; - keys[1] = keys[1] * 134775813L + 1; - keys[2] = updateChecksum(keys[2], ((int)keys[1]) >> 24); -} - -//! \internal Encrypts a byte array. -void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read) -{ - char t; - - for (qint64 i = 0; i < read; ++i) { - t = buffer[i]; - buffer[i] ^= decryptByte(keys[2]); - updateKeys(keys, t); - } -} - -namespace { -struct KeywordHelper { - const QString needle; - inline KeywordHelper(const QString& keyword) : needle(keyword) {} -}; - -bool operator<(const KeywordHelper& helper, const char* keyword) { - return helper.needle.compare(QLatin1String(keyword)) < 0; -} - -bool operator<(const char* keyword, const KeywordHelper& helper) { - return helper.needle.compare(QLatin1String(keyword)) > 0; -} - -bool hasExtension(const QString& ext, const char* const* map, int max) { - const char* const* start = &map[0]; - const char* const* end = &map[max - 1]; - const char* const* kw = qBinaryFind(start, end, KeywordHelper(ext)); - return kw != end; -} -} - -//! \internal Detects the best compression level for a given file extension. -Zip::CompressionLevel ZipPrivate::detectCompressionByMime(const QString& ext) -{ - // NOTE: Keep the MAX_* and the number of strings in the map up to date. - // NOTE: Alphabetically sort the strings in the map -- we use a binary search! - - // Archives or files that will hardly compress - const int MAX_EXT1 = 14; - const char* const ext1[MAX_EXT1] = { - "7z", "bin", "deb", "exe", "gz", "gz2", "jar", "rar", "rpm", "tar", "tgz", "z", "zip", - 0 // # MAX_EXT1 - }; - - // Slow or usually large files that we should not spend to much time with - const int MAX_EXT2 = 24; - const char* const ext2[MAX_EXT2] = { - "asf", - "avi", - "divx", - "doc", - "docx", - "flv", - "gif", - "iso", - "jpg", - "jpeg", - "mka", - "mkv", - "mp3", - "mp4", - "mpeg", - "mpg", - "odt", - "ogg", - "ogm", - "ra", - "rm", - "wma", - "wmv", - 0 // # MAX_EXT2 - }; - - // Files with high compression ratio - const int MAX_EXT3 = 28; - const char* const ext3[MAX_EXT3] = { - "asp", "bat", "c", "conf", "cpp", "cpp", "css", "csv", "cxx", "h", "hpp", "htm", "html", "hxx", - "ini", "js", "php", "pl", "py", "rtf", "sh", "tsv", "txt", "vb", "vbs", "xml", "xst", - 0 // # MAX_EXT3 - }; - - const char* const* map = ext1; - if (hasExtension(ext, map, MAX_EXT1)) - return Zip::Store; - - map = ext2; - if (hasExtension(ext, map, MAX_EXT2)) - return Zip::Deflate2; - - map = ext3; - if (hasExtension(ext, map, MAX_EXT3)) - return Zip::Deflate9; - - return Zip::Deflate5; -} - -/*! - Closes the current archive and writes out pending data. -*/ -Zip::ErrorCode ZipPrivate::closeArchive() -{ - if (!device) { - Q_ASSERT(!file); - return Zip::Ok; - } - - if (device != file) - disconnect(device, 0, this, 0); - - return do_closeArchive(); -} - -//! \internal -Zip::ErrorCode ZipPrivate::do_closeArchive() -{ - // Close current archive by writing out central directory - // and free up resources - - if (!device && !headers) - return Zip::Ok; - - quint32 szCentralDir = 0; - quint32 offCentralDir = device->pos(); - Zip::ErrorCode c = Zip::Ok; - - if (headers && device) { - for (QMap::ConstIterator itr = headers->constBegin(); - itr != headers->constEnd(); ++itr) { - const QString fileName = itr.key(); - const ZipEntryP* h = itr.value(); - c = writeEntry(fileName, h, szCentralDir); - } - } - - if (c == Zip::Ok) - c = writeCentralDir(offCentralDir, szCentralDir); - - if (c != Zip::Ok) { - if (file) { - file->close(); - if (!file->remove()) { - qDebug() << "Failed to delete corrupt archive."; - } - } - } - - return c; -} - -//! \internal -Zip::ErrorCode ZipPrivate::writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir) -{ - unsigned int sz; - - Q_ASSERT(h && device && headers); - - // signature - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x01; - buffer1[3] = 0x02; - - // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported) - buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0; - - // version needed to extract - buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION; - buffer1[ZIP_CD_OFF_VERSION + 1] = 0; - - // general purpose flag - buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0]; - buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1]; - - // compression method - buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF; - buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF; - - // last mod file time - buffer1[ZIP_CD_OFF_MODT] = h->modTime[0]; - buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1]; - - // last mod file date - buffer1[ZIP_CD_OFF_MODD] = h->modDate[0]; - buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1]; - - // crc (4bytes) [16,17,18,19] - setULong(h->crc, buffer1, ZIP_CD_OFF_CRC); - - // compressed size (4bytes: [20,21,22,23]) - setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE); - - // uncompressed size [24,25,26,27] - setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE); - - // filename - QByteArray fileNameBytes = fileName.toLatin1(); - sz = fileNameBytes.size(); - buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF; - buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; - - // extra field length - buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0; - - // file comment length - buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0; - - // disk number start - buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0; - - // internal file attributes - buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0; - - // external file attributes - buffer1[ZIP_CD_OFF_EATTR] = - buffer1[ZIP_CD_OFF_EATTR + 1] = - buffer1[ZIP_CD_OFF_EATTR + 2] = - buffer1[ZIP_CD_OFF_EATTR + 3] = 0; - - // relative offset of local header [42->45] - setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF); - - if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE) { - return Zip::WriteFailed; - } - - // Write out filename - if ((unsigned int)device->write(fileNameBytes) != sz) { - return Zip::WriteFailed; - } - - szCentralDir += (ZIP_CD_SIZE + sz); - - return Zip::Ok; -} - -//! \internal -Zip::ErrorCode ZipPrivate::writeCentralDir(quint32 offCentralDir, quint32 szCentralDir) -{ - Q_ASSERT(device && headers); - - unsigned int sz; - - // signature - buffer1[0] = 'P'; - buffer1[1] = 'K'; - buffer1[2] = 0x05; - buffer1[3] = 0x06; - - // number of this disk - buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0; - - // number of disk with central directory - buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0; - - // number of entries in this disk - sz = headers->count(); - buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF; - buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF; - - // total number of entries - buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES]; - buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1]; - - // size of central directory [12->15] - setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE); - - // central dir offset [16->19] - setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF); - - // ZIP file comment length - QByteArray commentBytes = comment.toLatin1(); - quint16 commentLength = commentBytes.size(); - - if (commentLength == 0) { - buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0; - } else { - buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF; - buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF; - } - - if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE) { - return Zip::WriteFailed; - } - - if (commentLength != 0) { - if ((unsigned int)device->write(commentBytes) != commentLength) { - return Zip::WriteFailed; - } - } - - return Zip::Ok; -} - -//! \internal -void ZipPrivate::reset() -{ - comment.clear(); - - if (headers) { - qDeleteAll(*headers); - delete headers; - headers = 0; - } - - device = 0; - - if (file) - delete file; - file = 0; -} - -//! \internal Returns the path of the parent directory -QString ZipPrivate::extractRoot(const QString& p, Zip::CompressionOptions o) -{ - Q_UNUSED(o); - QDir d(QDir::cleanPath(p)); - if (!d.exists()) - return QString(); - - if (!d.cdUp()) - return QString(); - - return d.absolutePath(); -} - - -/************************************************************************ - Public interface -*************************************************************************/ - -/*! - Creates a new Zip file compressor. -*/ -Zip::Zip() : d(new ZipPrivate) -{ -} - -/*! - Closes any open archive and releases used resources. -*/ -Zip::~Zip() -{ - closeArchive(); - delete d; -} - -/*! - Returns true if there is an open archive. -*/ -bool Zip::isOpen() const -{ - return d->device; -} - -/*! - Sets the password to be used for the next files being added! - Files added before calling this method will use the previously - set password (if any). - Closing the archive won't clear the password! -*/ -void Zip::setPassword(const QString& pwd) -{ - d->password = pwd; -} - -//! Convenience method, clears the current password. -void Zip::clearPassword() -{ - d->password.clear(); -} - -//! Returns the currently used password. -QString Zip::password() const -{ - return d->password; -} - -/*! - Attempts to create a new Zip archive. If \p overwrite is true and the file - already exist it will be overwritten. - Any open archive will be closed. - */ -Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite) -{ - closeArchive(); - Q_ASSERT(!d->device && !d->file); - - if (filename.isEmpty()) - return Zip::FileNotFound; - - d->file = new QFile(filename); - - if (d->file->exists() && !overwrite) { - delete d->file; - d->file = 0; - return Zip::FileExists; - } - - if (!d->file->open(QIODevice::WriteOnly)) { - delete d->file; - d->file = 0; - return Zip::OpenFailed; - } - - const Zip::ErrorCode ec = createArchive(d->file); - if (ec != Zip::Ok) { - closeArchive(); - } - - return ec; -} - -/*! - Attempts to create a new Zip archive. If there is another open archive this will be closed. - \warning The class takes ownership of the device! - */ -Zip::ErrorCode Zip::createArchive(QIODevice* device) -{ - if (!device) { - qDebug() << "Invalid device."; - return Zip::OpenFailed; - } - - return d->createArchive(device); -} - -/*! - Returns the current archive comment. -*/ -QString Zip::archiveComment() const -{ - return d->comment; -} - -/*! - Sets the comment for this archive. Note: createArchive() should have been - called before. -*/ -void Zip::setArchiveComment(const QString& comment) -{ - d->comment = comment; -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::IgnorePaths flag as compression option and an empty \p root parameter. - - The result is that all files found in \p path (and in subdirectories) are - added to the zip file without a directory entry. -*/ -Zip::ErrorCode Zip::addDirectoryContents(const QString& path, CompressionLevel level) -{ - return addDirectory(path, QString(), IgnorePaths, level); -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::IgnorePaths flag as compression option. - - The result is that all files found in \p path (and in subdirectories) are - added to the zip file without a directory entry (or within a directory - structure specified by \p root). -*/ -Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level) -{ - return addDirectory(path, root, IgnorePaths, level); -} - -/*! - Convenience method, same as calling - Zip::addDirectory(const QString&,const QString&,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionLevel level) -{ - return addDirectory(path, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level) -{ - return addDirectory(path, root, Zip::RelativePaths, level); -} - -/*! - Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder. - Stops adding files if some error occurs. - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). - - If \p addedFiles is not null it is set to the number of successfully added - files. -*/ -Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level, int* addedFiles) -{ - const int hierarchyLev = 0; - return d->addDirectory(path, root, options, level, hierarchyLev, addedFiles); -} - -/*! - Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths as compression option. - */ -Zip::ErrorCode Zip::addFile(const QString& path, CompressionLevel level) -{ - return addFile(path, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, - CompressionLevel level) -{ - return addFile(path, root, Zip::RelativePaths, level); -} - -/*! - Adds the file at \p path to the archive, using \p root as name for the root folder. - If \p path points to a directory the behaviour is basically the same as - addDirectory(). - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). -*/ -Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level) -{ - if (path.isEmpty()) - return Zip::Ok; - return addFiles(QStringList() << path, root, options, level); -} - -/*! - Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) - with an empty \p root parameter and Zip::RelativePaths as compression option. - */ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, CompressionLevel level) -{ - return addFiles(paths, QString(), Zip::RelativePaths, level); -} - -/*! - Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) - with the Zip::RelativePaths flag as compression option. - */ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, - CompressionLevel level) -{ - return addFiles(paths, root, Zip::RelativePaths, level); -} - -/*! - Adds the files or directories in \p paths to the archive, using \p root as - name for the root folder. - This is similar to calling addFile or addDirectory for all the entries in - \p paths, except it is slightly faster. - - The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. - This means that the last one overwrites the previous one (if some conflict occurs), i.e. - Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. - - The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / - is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). - - If \p addedFiles is not null it is set to the number of successfully added - files. -*/ -Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, - CompressionOptions options, CompressionLevel level, int* addedFiles) -{ - return d->addFiles(paths, root, options, level, addedFiles); -} - -/*! - Closes the archive and writes any pending data. -*/ -Zip::ErrorCode Zip::closeArchive() -{ - Zip::ErrorCode ec = d->closeArchive(); - d->reset(); - return ec; -} - -/*! - Returns a locale translated error string for a given error code. -*/ -QString Zip::formatError(Zip::ErrorCode c) const -{ - switch (c) - { - case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break; - case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break; - case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break; - case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break; - case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break; - case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break; - case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break; - case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break; - case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break; - default: ; - } - - return QCoreApplication::translate("Zip", "Unknown error."); -} - -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: zip.cpp +** Last updated [dd/mm/yyyy]: 01/02/2007 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "zip.h" +#include "zip_p.h" +#include "zipentry_p.h" + +// we only use this to seed the random number generator +#include + +#include +#include +#include +#include +#include +#include +#include + +// You can remove this #include if you replace the qDebug() statements. +#include + + +/*! #define OSDAB_ZIP_NO_PNG_RLE to disable the use of Z_RLE compression strategy with + PNG files (achieves slightly better compression levels according to the authors). +*/ +// #define OSDAB_ZIP_NO_PNG_RLE + +#define OSDAB_ZIP_NO_DEBUG + +//! Local header size (including signature, excluding variable length fields) +#define ZIP_LOCAL_HEADER_SIZE 30 +//! Encryption header size +#define ZIP_LOCAL_ENC_HEADER_SIZE 12 +//! Data descriptor size (signature included) +#define ZIP_DD_SIZE_WS 16 +//! Central Directory record size (signature included) +#define ZIP_CD_SIZE 46 +//! End of Central Directory record size (signature included) +#define ZIP_EOCD_SIZE 22 + +// Some offsets inside a local header record (signature included) +#define ZIP_LH_OFF_VERS 4 +#define ZIP_LH_OFF_GPFLAG 6 +#define ZIP_LH_OFF_CMET 8 +#define ZIP_LH_OFF_MODT 10 +#define ZIP_LH_OFF_MODD 12 +#define ZIP_LH_OFF_CRC 14 +#define ZIP_LH_OFF_CSIZE 18 +#define ZIP_LH_OFF_USIZE 22 +#define ZIP_LH_OFF_NAMELEN 26 +#define ZIP_LH_OFF_XLEN 28 + +// Some offsets inside a data descriptor record (including signature) +#define ZIP_DD_OFF_CRC32 4 +#define ZIP_DD_OFF_CSIZE 8 +#define ZIP_DD_OFF_USIZE 12 + +// Some offsets inside a Central Directory record (including signature) +#define ZIP_CD_OFF_MADEBY 4 +#define ZIP_CD_OFF_VERSION 6 +#define ZIP_CD_OFF_GPFLAG 8 +#define ZIP_CD_OFF_CMET 10 +#define ZIP_CD_OFF_MODT 12 +#define ZIP_CD_OFF_MODD 14 +#define ZIP_CD_OFF_CRC 16 +#define ZIP_CD_OFF_CSIZE 20 +#define ZIP_CD_OFF_USIZE 24 +#define ZIP_CD_OFF_NAMELEN 28 +#define ZIP_CD_OFF_XLEN 30 +#define ZIP_CD_OFF_COMMLEN 32 +#define ZIP_CD_OFF_DISKSTART 34 +#define ZIP_CD_OFF_IATTR 36 +#define ZIP_CD_OFF_EATTR 38 +#define ZIP_CD_OFF_LHOFF 42 + +// Some offsets inside a EOCD record (including signature) +#define ZIP_EOCD_OFF_DISKNUM 4 +#define ZIP_EOCD_OFF_CDDISKNUM 6 +#define ZIP_EOCD_OFF_ENTRIES 8 +#define ZIP_EOCD_OFF_CDENTRIES 10 +#define ZIP_EOCD_OFF_CDSIZE 12 +#define ZIP_EOCD_OFF_CDOFF 16 +#define ZIP_EOCD_OFF_COMMLEN 20 + +//! PKZip version for archives created by this API +#define ZIP_VERSION 0x14 + +//! Do not store very small files as the compression headers overhead would be to big +#define ZIP_COMPRESSION_THRESHOLD 60 + +/*! + \class Zip zip.h + + \brief Zip file compression. + + Some quick usage examples. + + \verbatim + Suppose you have this directory structure: + + /home/user/dir1/file1.1 + /home/user/dir1/file1.2 + /home/user/dir1/dir1.1/ + /home/user/dir1/dir1.2/file1.2.1 + + EXAMPLE 1: + myZipInstance.addDirectory("/home/user/dir1"); + + RESULT: + Beheaves like any common zip software and creates a zip file with this structure: + + dir1/file1.1 + dir1/file1.2 + dir1/dir1.1/ + dir1/dir1.2/file1.2.1 + + EXAMPLE 2: + myZipInstance.addDirectory("/home/user/dir1", "myRoot/myFolder"); + + RESULT: + Adds a custom root to the paths and creates a zip file with this structure: + + myRoot/myFolder/dir1/file1.1 + myRoot/myFolder/dir1/file1.2 + myRoot/myFolder/dir1/dir1.1/ + myRoot/myFolder/dir1/dir1.2/file1.2.1 + + EXAMPLE 3: + myZipInstance.addDirectory("/home/user/dir1", Zip::AbsolutePaths); + + NOTE: + Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH). + + RESULT: + Preserves absolute paths and creates a zip file with this structure: + + /home/user/dir1/file1.1 + /home/user/dir1/file1.2 + /home/user/dir1/dir1.1/ + /home/user/dir1/dir1.2/file1.2.1 + + EXAMPLE 4: + myZipInstance.setPassword("hellopass"); + myZipInstance.addDirectory("/home/user/dir1", "/"); + + RESULT: + Adds and encrypts the files in /home/user/dir1, creating the following zip structure: + + /dir1/file1.1 + /dir1/file1.2 + /dir1/dir1.1/ + /dir1/dir1.2/file1.2.1 + + EXAMPLE 5: + myZipInstance.addDirectory("/home/user/dir1", Zip::IgnoreRoot); + + RESULT: + Adds the files in /home/user/dir1 but doesn't create the top level + directory: + + file1.1 + file1.2 + dir1.1/ + dir1.2/file1.2.1 + + EXAMPLE 5: + myZipInstance.addDirectory("/home/user/dir1", "data/backup", Zip::IgnoreRoot); + + RESULT: + Adds the files in /home/user/dir1 but uses "data/backup" as top level + directory instead of "dir1": + + data/backup/file1.1 + data/backup/file1.2 + data/backup/dir1.1/ + data/backup/dir1.2/file1.2.1 + + \endverbatim +*/ + +/*! \enum Zip::ErrorCode The result of a compression operation. + \value Zip::Ok No error occurred. + \value Zip::ZlibInit Failed to init or load the zlib library. + \value Zip::ZlibError The zlib library returned some error. + \value Zip::FileExists The file already exists and will not be overwritten. + \value Zip::OpenFailed Unable to create or open a device. + \value Zip::NoOpenArchive CreateArchive() has not been called yet. + \value Zip::FileNotFound File or directory does not exist. + \value Zip::ReadFailed Reading of a file failed. + \value Zip::WriteFailed Writing of a file failed. + \value Zip::SeekFailed Seek failed. +*/ + +/*! \enum Zip::CompressionLevel Returns the result of a decompression operation. + \value Zip::Store No compression. + \value Zip::Deflate1 Deflate compression level 1(lowest compression). + \value Zip::Deflate1 Deflate compression level 2. + \value Zip::Deflate1 Deflate compression level 3. + \value Zip::Deflate1 Deflate compression level 4. + \value Zip::Deflate1 Deflate compression level 5. + \value Zip::Deflate1 Deflate compression level 6. + \value Zip::Deflate1 Deflate compression level 7. + \value Zip::Deflate1 Deflate compression level 8. + \value Zip::Deflate1 Deflate compression level 9 (maximum compression). + \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression). + \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed. + \value Zip::AutoFull Use both CPU and MIME type detection. +*/ + +namespace { + +struct ZippedDir { + bool init; + QString actualRoot; + int files; + ZippedDir() : init(false), actualRoot(), files(0) {} +}; + +void checkRootPath(QString& path) +{ + const bool isUnixRoot = path.length() == 1 && path.at(0) == QLatin1Char('/'); + if (!path.isEmpty() && !isUnixRoot) { + while (path.endsWith(QLatin1String("\\"))) + path.truncate(path.length() - 1); + + int sepCount = 0; + for (int i = path.length()-1; i >= 0; --i) { + if (path.at(i) == QLatin1Char('/')) + ++sepCount; + else break; + } + + if (sepCount > 1) + path.truncate(path.length() - (sepCount-1)); + else if (sepCount == 0) + path.append(QLatin1String("/")); + } +} + +} + +////////////////////////////////////////////////////////////////////////// + +OSDAB_BEGIN_NAMESPACE(Zip) + +/************************************************************************ + Private interface +*************************************************************************/ + +//! \internal +ZipPrivate::ZipPrivate() : + headers(0), + device(0), + file(0), + uBuffer(0), + crcTable(0), + comment(), + password() +{ + // keep an unsigned pointer so we avoid to over bloat the code with casts + uBuffer = (unsigned char*) buffer1; + crcTable = get_crc_table(); +} + +//! \internal +ZipPrivate::~ZipPrivate() +{ + closeArchive(); +} + +//! \internal +Zip::ErrorCode ZipPrivate::createArchive(QIODevice* dev) +{ + Q_ASSERT(dev); + + if (device) + closeArchive(); + + device = dev; + if (device != file) + connect(device, SIGNAL(destroyed(QObject*)), this, SLOT(deviceDestroyed(QObject*))); + + if (!device->isOpen()) { + if (!device->open(QIODevice::ReadOnly)) { + delete device; + device = 0; + qDebug() << "Unable to open device for writing."; + return Zip::OpenFailed; + } + } + + headers = new QMap; + return Zip::Ok; +} + +//! \internal +void ZipPrivate::deviceDestroyed(QObject*) +{ + qDebug("Unexpected device destruction detected."); + do_closeArchive(); +} + +/*! Returns true if an entry for \p info has already been added. + Uses file size and lower case absolute path to compare entries. +*/ +bool ZipPrivate::containsEntry(const QFileInfo& info) const +{ + if (!headers || headers->isEmpty()) + return false; + + const qint64 sz = info.size(); + const QString path = info.absoluteFilePath().toLower(); + + QMap::ConstIterator b = headers->constBegin(); + const QMap::ConstIterator e = headers->constEnd(); + while (b != e) { + const ZipEntryP* e = b.value(); + if (e->fileSize == sz && e->absolutePath == path) + return true; + ++b; + } + + return false; +} + +//! \internal Actual implementation of the addDirectory* methods. +Zip::ErrorCode ZipPrivate::addDirectory(const QString& path, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, int hierarchyLevel, + int* addedFiles) +{ + if (addedFiles) + ++(*addedFiles); + + // Bad boy didn't call createArchive() yet :) + if (!device) + return Zip::NoOpenArchive; + + QDir dir(path); + if (!dir.exists()) + return Zip::FileNotFound; + + // Remove any trailing separator + QString actualRoot = root.trimmed(); + + // Preserve Unix root but make sure the path ends only with a single + // unix like separator + ::checkRootPath(actualRoot); + + // QDir::cleanPath() fixes some issues with QDir::dirName() + QFileInfo current(QDir::cleanPath(path)); + + const bool path_absolute = options.testFlag(Zip::AbsolutePaths); + const bool path_ignore = options.testFlag(Zip::IgnorePaths); + const bool path_noroot = options.testFlag(Zip::IgnoreRoot); + + if (path_absolute && !path_ignore && !path_noroot) { + QString absolutePath = extractRoot(path, options); + if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) + absolutePath.append(QLatin1String("/")); + actualRoot.append(absolutePath); + } + + const bool skipDirName = !hierarchyLevel && path_noroot; + if (!path_ignore && !skipDirName) { + actualRoot.append(QDir(current.absoluteFilePath()).dirName()); + actualRoot.append(QLatin1String("/")); + } + + // actualRoot now contains the path of the file relative to the zip archive + // with a trailing / + + const bool skipBad = options & Zip::SkipBadFiles; + const bool noDups = options & Zip::CheckForDuplicates; + + const QDir::Filters dir_filter = + QDir::Files | + QDir::Dirs | + QDir::NoDotAndDotDot | + QDir::NoSymLinks; + const QDir::SortFlags dir_sort = + QDir::DirsFirst; + QFileInfoList list = dir.entryInfoList(dir_filter, dir_sort); + + Zip::ErrorCode ec = Zip::Ok; + bool filesAdded = false; + + Zip::CompressionOptions recursionOptions; + if (path_ignore) + recursionOptions |= Zip::IgnorePaths; + else recursionOptions |= Zip::RelativePaths; + + for (int i = 0; i < list.size(); ++i) { + QFileInfo info = list.at(i); + const QString absPath = info.absoluteFilePath(); + if (noDups && containsEntry(info)) + continue; + if (info.isDir()) { + // Recursion + ec = addDirectory(absPath, actualRoot, recursionOptions, + level, hierarchyLevel + 1, addedFiles); + } else { + ec = createEntry(info, actualRoot, level); + if (ec == Zip::Ok) { + filesAdded = true; + if (addedFiles) + ++(*addedFiles); + } + } + + if (ec != Zip::Ok && !skipBad) { + break; + } + } + + // We need an explicit record for this dir + // Non-empty directories don't need it because they have a path component in the filename + if (!filesAdded && !path_ignore) + ec = createEntry(current, actualRoot, level); + + return ec; +} + +//! \internal Actual implementation of the addFile methods. +Zip::ErrorCode ZipPrivate::addFiles(const QStringList& files, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int* addedFiles) +{ + if (addedFiles) + *addedFiles = 0; + + const bool skipBad = options & Zip::SkipBadFiles; + const bool noDups = options & Zip::CheckForDuplicates; + + // Bad boy didn't call createArchive() yet :) + if (!device) + return Zip::NoOpenArchive; + + QFileInfoList paths; + paths.reserve(files.size()); + for (int i = 0; i < files.size(); ++i) { + QFileInfo info(files.at(i)); + if (noDups && (paths.contains(info) || containsEntry(info))) + continue; + if (!info.exists() || !info.isReadable()) { + if (skipBad) { + continue; + } else { + return Zip::FileNotFound; + } + } + paths.append(info); + } + + if (paths.isEmpty()) + return Zip::Ok; + + // Remove any trailing separator + QString actualRoot = root.trimmed(); + + // Preserve Unix root but make sure the path ends only with a single + // unix like separator + ::checkRootPath(actualRoot); + + const bool path_absolute = options.testFlag(Zip::AbsolutePaths); + const bool path_ignore = options.testFlag(Zip::IgnorePaths); + const bool path_noroot = options.testFlag(Zip::IgnoreRoot); + + Zip::ErrorCode ec = Zip::Ok; + QHash dirMap; + + for (int i = 0; i < paths.size(); ++i) { + const QFileInfo& info = paths.at(i); + const QString path = QFileInfo(QDir::cleanPath(info.absolutePath())).absolutePath(); + + ZippedDir& zd = dirMap[path]; + if (!zd.init) { + zd.init = true; + zd.actualRoot = actualRoot; + if (path_absolute && !path_ignore && !path_noroot) { + QString absolutePath = extractRoot(path, options); + if (!absolutePath.isEmpty() && absolutePath != QLatin1String("/")) + absolutePath.append(QLatin1String("/")); + zd.actualRoot.append(absolutePath); + } + + if (!path_ignore && !path_noroot) { + zd.actualRoot.append(QDir(path).dirName()); + zd.actualRoot.append(QLatin1String("/")); + } + } + + // zd.actualRoot now contains the path of the file relative to the zip archive + // with a trailing / + + if (info.isDir()) { + // Recursion + ec = addDirectory(info.absoluteFilePath(), actualRoot, options, + level, 1, addedFiles); + } else { + ec = createEntry(info, actualRoot, level); + if (ec == Zip::Ok) { + ++zd.files; + if (addedFiles) + ++(*addedFiles); + } + } + + if (ec != Zip::Ok && !skipBad) { + break; + } + } + + // Create explicit records for empty directories + if (!path_ignore) { + QHash::ConstIterator b = dirMap.constBegin(); + const QHash::ConstIterator e = dirMap.constEnd(); + while (b != e) { + const ZippedDir& zd = b.value(); + if (zd.files <= 0) { + ec = createEntry(b.key(), zd.actualRoot, level); + } + ++b; + } + } + + return ec; +} + +//! \internal \p file must be a file and not a directory. +Zip::ErrorCode ZipPrivate::deflateFile(const QFileInfo& fileInfo, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys) +{ + const QString path = fileInfo.absoluteFilePath(); + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << QString("An error occurred while opening %1").arg(path); + return Zip::OpenFailed; + } + + const Zip::ErrorCode ec = (level == Zip::Store) + ? storeFile(path, file, crc, written, keys) + : compressFile(path, file, crc, written, level, keys); + + file.close(); + return ec; +} + +//! \internal +Zip::ErrorCode ZipPrivate::storeFile(const QString& path, QIODevice& file, + quint32& crc, qint64& totalWritten, quint32** keys) +{ + Q_UNUSED(path); + + qint64 read = 0; + qint64 written = 0; + + const bool encrypt = keys != 0; + + totalWritten = 0; + crc = crc32(0L, Z_NULL, 0); + + while ( (read = file.read(buffer1, ZIP_READ_BUFFER)) > 0 ) { + crc = crc32(crc, uBuffer, read); + if (encrypt) + encryptBytes(*keys, buffer1, read); + written = device->write(buffer1, read); + totalWritten += written; + if (written != read) { + return Zip::WriteFailed; + } + } + + return Zip::Ok; +} + +//! \internal +int ZipPrivate::compressionStrategy(const QString& path, QIODevice& file) const +{ + Q_UNUSED(file); + +#ifndef OSDAB_ZIP_NO_PNG_RLE + return Z_DEFAULT_STRATEGY; +#endif + const bool isPng = path.endsWith(QLatin1String("png"), Qt::CaseInsensitive); + return isPng ? Z_RLE : Z_DEFAULT_STRATEGY; +} + +//! \internal +Zip::ErrorCode ZipPrivate::compressFile(const QString& path, QIODevice& file, + quint32& crc, qint64& totalWritten, const Zip::CompressionLevel& level, quint32** keys) +{ + qint64 read = 0; + qint64 written = 0; + + qint64 totRead = 0; + qint64 toRead = file.size(); + + const bool encrypt = keys != 0; + const int strategy = compressionStrategy(path, file); + + totalWritten = 0; + crc = crc32(0L, Z_NULL, 0); + + z_stream zstr; + + // Initialize zalloc, zfree and opaque before calling the init function + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + + int zret; + + // Use deflateInit2 with negative windowBits to get raw compression + if ((zret = deflateInit2_( + &zstr, + (int)level, // compression level + Z_DEFLATED, // method + -MAX_WBITS, // windowBits + 8, // memLevel + strategy, + ZLIB_VERSION, + sizeof(z_stream) + )) != Z_OK ) { + qDebug() << "Could not initialize zlib for compression"; + return Zip::ZlibError; + } + + qint64 compressed; + int flush = Z_NO_FLUSH; + do { + read = file.read(buffer1, ZIP_READ_BUFFER); + totRead += read; + if (!read) + break; + + if (read < 0) { + deflateEnd(&zstr); + qDebug() << QString("Error while reading %1").arg(path); + return Zip::ReadFailed; + } + + crc = crc32(crc, uBuffer, read); + + zstr.next_in = (Bytef*) buffer1; + zstr.avail_in = (uInt)read; + + // Tell zlib if this is the last chunk we want to encode + // by setting the flush parameter to Z_FINISH + flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH; + + // Run deflate() on input until output buffer not full + // finish compression if all of source has been read in + do { + zstr.next_out = (Bytef*) buffer2; + zstr.avail_out = ZIP_READ_BUFFER; + + zret = deflate(&zstr, flush); + // State not clobbered + Q_ASSERT(zret != Z_STREAM_ERROR); + + // Write compressed data to file and empty buffer + compressed = ZIP_READ_BUFFER - zstr.avail_out; + + if (encrypt) + encryptBytes(*keys, buffer2, compressed); + + written = device->write(buffer2, compressed); + totalWritten += written; + + if (written != compressed) { + deflateEnd(&zstr); + qDebug() << QString("Error while writing %1").arg(path); + return Zip::WriteFailed; + } + + } while (zstr.avail_out == 0); + + // All input will be used + Q_ASSERT(zstr.avail_in == 0); + + } while (flush != Z_FINISH); + + // Stream will be complete + Q_ASSERT(zret == Z_STREAM_END); + deflateEnd(&zstr); + + return Zip::Ok; +} + +//! \internal Writes a new entry in the zip file. +Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, + Zip::CompressionLevel level) +{ + const bool dirOnly = file.isDir(); + + // entryName contains the path as it should be written + // in the zip file records + const QString entryName = dirOnly + ? root + : root + file.fileName(); + + // Directory entry + if (dirOnly || file.size() < ZIP_COMPRESSION_THRESHOLD) { + level = Zip::Store; + } else { + switch (level) { + case Zip::AutoCPU: + level = Zip::Deflate5; +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + case Zip::AutoMIME: + level = detectCompressionByMime(file.completeSuffix().toLower()); +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + case Zip::AutoFull: + level = detectCompressionByMime(file.completeSuffix().toLower()); +#ifndef OSDAB_ZIP_NO_DEBUG + qDebug("Compression level for '%s': %d", entryName.toLatin1().constData(), (int)level); +#endif + break; + default: ; + } + } + + + + // create header and store it to write a central directory later + QScopedPointer h(new ZipEntryP); + h->absolutePath = file.absoluteFilePath().toLower(); + h->fileSize = file.size(); + + // Set encryption bit and set the data descriptor bit + // so we can use mod time instead of crc for password check + bool encrypt = !dirOnly && !password.isEmpty(); + if (encrypt) + h->gpFlag[0] |= 9; + + QDateTime dt = file.lastModified(); + dt = OSDAB_ZIP_MANGLE(fromFileTimestamp)(dt); + QDate d = dt.date(); + h->modDate[1] = ((d.year() - 1980) << 1) & 254; + h->modDate[1] |= ((d.month() >> 3) & 1); + h->modDate[0] = ((d.month() & 7) << 5) & 224; + h->modDate[0] |= d.day(); + + QTime t = dt.time(); + h->modTime[1] = (t.hour() << 3) & 248; + h->modTime[1] |= ((t.minute() >> 3) & 7); + h->modTime[0] = ((t.minute() & 7) << 5) & 224; + h->modTime[0] |= t.second() / 2; + + h->szUncomp = dirOnly ? 0 : file.size(); + + h->compMethod = (level == Zip::Store) ? 0 : 0x0008; + + // **** Write local file header **** + + // signature + buffer1[0] = 'P'; buffer1[1] = 'K'; + buffer1[2] = 0x3; buffer1[3] = 0x4; + + // version needed to extract + buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION; + buffer1[ZIP_LH_OFF_VERS + 1] = 0; + + // general purpose flag + buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0]; + buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1]; + + // compression method + buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF; + buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF; + + // last mod file time + buffer1[ZIP_LH_OFF_MODT] = h->modTime[0]; + buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1]; + + // last mod file date + buffer1[ZIP_LH_OFF_MODD] = h->modDate[0]; + buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1]; + + // skip crc (4bytes) [14,15,16,17] + + // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21]) + buffer1[ZIP_LH_OFF_CSIZE] = + buffer1[ZIP_LH_OFF_CSIZE + 1] = + buffer1[ZIP_LH_OFF_CSIZE + 2] = + buffer1[ZIP_LH_OFF_CSIZE + 3] = 0; + + h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0; + + // uncompressed size [22,23,24,25] + setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE); + + // filename length + QByteArray entryNameBytes = entryName.toLatin1(); + int sz = entryNameBytes.size(); + + buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF; + buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; + + // extra field length + buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0; + + // Store offset to write crc and compressed size + h->lhOffset = device->pos(); + quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC; + + if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE) { + return Zip::WriteFailed; + } + + // Write out filename + if (device->write(entryNameBytes) != sz) { + return Zip::WriteFailed; + } + + // Encryption keys + quint32 keys[3] = { 0, 0, 0 }; + + if (encrypt) { + // **** encryption header **** + + // XOR with PI to ensure better random numbers + // with poorly implemented rand() as suggested by Info-Zip + srand(time(NULL) ^ 3141592654UL); + int randByte; + + initKeys(keys); + for (int i = 0; i < 10; ++i) { + randByte = (rand() >> 7) & 0xff; + buffer1[i] = decryptByte(keys[2]) ^ randByte; + updateKeys(keys, randByte); + } + + // Encrypt encryption header + initKeys(keys); + for (int i = 0; i < 10; ++i) { + randByte = decryptByte(keys[2]); + updateKeys(keys, buffer1[i]); + buffer1[i] ^= randByte; + } + + // We don't know the CRC at this time, so we use the modification time + // as the last two bytes + randByte = decryptByte(keys[2]); + updateKeys(keys, h->modTime[0]); + buffer1[10] ^= randByte; + + randByte = decryptByte(keys[2]); + updateKeys(keys, h->modTime[1]); + buffer1[11] ^= randByte; + + // Write out encryption header + if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE) { + return Zip::WriteFailed; + } + } + + quint32 crc = 0; + qint64 written = 0; + + if (!dirOnly) { + quint32* k = keys; + const Zip::ErrorCode ec = deflateFile(file, crc, written, level, encrypt ? &k : 0); + if (ec != Zip::Ok) + return ec; + Q_ASSERT(!h.isNull()); + } + + // Store end of entry offset + quint32 current = device->pos(); + + // Update crc and compressed size in local header + if (!device->seek(crcOffset)) { + return Zip::SeekFailed; + } + + h->crc = dirOnly ? 0 : crc; + h->szComp += written; + + setULong(h->crc, buffer1, 0); + setULong(h->szComp, buffer1, 4); + if ( device->write(buffer1, 8) != 8) { + return Zip::WriteFailed; + } + + // Seek to end of entry + if (!device->seek(current)) { + return Zip::SeekFailed; + } + + if ((h->gpFlag[0] & 8) == 8) { + // Write data descriptor + + // Signature: PK\7\8 + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x07; + buffer1[3] = 0x08; + + // CRC + setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32); + + // Compressed size + setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE); + + // Uncompressed size + setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE); + + if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS) { + return Zip::WriteFailed; + } + } + + headers->insert(entryName, h.take()); + return Zip::Ok; +} + +//! \internal +int ZipPrivate::decryptByte(quint32 key2) const +{ + quint16 temp = ((quint16)(key2) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +//! \internal Writes an quint32 (4 bytes) to a byte array at given offset. +void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset) +{ + buffer[offset+3] = ((v >> 24) & 0xFF); + buffer[offset+2] = ((v >> 16) & 0xFF); + buffer[offset+1] = ((v >> 8) & 0xFF); + buffer[offset] = (v & 0xFF); +} + +//! \internal Initializes decryption keys using a password. +void ZipPrivate::initKeys(quint32* keys) const +{ + // Encryption keys initialization constants are taken from the + // PKZip file format specification docs + keys[0] = 305419896L; + keys[1] = 591751049L; + keys[2] = 878082192L; + + QByteArray pwdBytes = password.toLatin1(); + int sz = pwdBytes.size(); + const char* ascii = pwdBytes.data(); + + for (int i = 0; i < sz; ++i) + updateKeys(keys, (int)ascii[i]); +} + +//! Updates a one-char-only CRC; it's the Info-Zip macro re-adapted. +quint32 ZipPrivate::updateChecksum(const quint32& crc, const quint32& val) const +{ + return quint32(crcTable[quint32(crc^val) & 0xff] ^ crc_t(crc >> 8)); +} + +//! \internal Updates encryption keys. +void ZipPrivate::updateKeys(quint32* keys, int c) const +{ + keys[0] = updateChecksum(keys[0], c); + keys[1] += keys[0] & 0xff; + keys[1] = keys[1] * 134775813L + 1; + keys[2] = updateChecksum(keys[2], ((int)keys[1]) >> 24); +} + +//! \internal Encrypts a byte array. +void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read) +{ + char t; + + for (qint64 i = 0; i < read; ++i) { + t = buffer[i]; + buffer[i] ^= decryptByte(keys[2]); + updateKeys(keys, t); + } +} + +namespace { +struct KeywordHelper { + const QString needle; + inline KeywordHelper(const QString& keyword) : needle(keyword) {} +}; + +bool operator<(const KeywordHelper& helper, const char* keyword) { + return helper.needle.compare(QLatin1String(keyword)) < 0; +} + +bool operator<(const char* keyword, const KeywordHelper& helper) { + return helper.needle.compare(QLatin1String(keyword)) > 0; +} + +bool hasExtension(const QString& ext, const char* const* map, int max) { + const char* const* start = &map[0]; + const char* const* end = &map[max - 1]; + const char* const* kw = qBinaryFind(start, end, KeywordHelper(ext)); + return kw != end; +} +} + +//! \internal Detects the best compression level for a given file extension. +Zip::CompressionLevel ZipPrivate::detectCompressionByMime(const QString& ext) +{ + // NOTE: Keep the MAX_* and the number of strings in the map up to date. + // NOTE: Alphabetically sort the strings in the map -- we use a binary search! + + // Archives or files that will hardly compress + const int MAX_EXT1 = 14; + const char* const ext1[MAX_EXT1] = { + "7z", "bin", "deb", "exe", "gz", "gz2", "jar", "rar", "rpm", "tar", "tgz", "z", "zip", + 0 // # MAX_EXT1 + }; + + // Slow or usually large files that we should not spend to much time with + const int MAX_EXT2 = 24; + const char* const ext2[MAX_EXT2] = { + "asf", + "avi", + "divx", + "doc", + "docx", + "flv", + "gif", + "iso", + "jpg", + "jpeg", + "mka", + "mkv", + "mp3", + "mp4", + "mpeg", + "mpg", + "odt", + "ogg", + "ogm", + "ra", + "rm", + "wma", + "wmv", + 0 // # MAX_EXT2 + }; + + // Files with high compression ratio + const int MAX_EXT3 = 28; + const char* const ext3[MAX_EXT3] = { + "asp", "bat", "c", "conf", "cpp", "cpp", "css", "csv", "cxx", "h", "hpp", "htm", "html", "hxx", + "ini", "js", "php", "pl", "py", "rtf", "sh", "tsv", "txt", "vb", "vbs", "xml", "xst", + 0 // # MAX_EXT3 + }; + + const char* const* map = ext1; + if (hasExtension(ext, map, MAX_EXT1)) + return Zip::Store; + + map = ext2; + if (hasExtension(ext, map, MAX_EXT2)) + return Zip::Deflate2; + + map = ext3; + if (hasExtension(ext, map, MAX_EXT3)) + return Zip::Deflate9; + + return Zip::Deflate5; +} + +/*! + Closes the current archive and writes out pending data. +*/ +Zip::ErrorCode ZipPrivate::closeArchive() +{ + if (!device) { + Q_ASSERT(!file); + return Zip::Ok; + } + + if (device != file) + disconnect(device, 0, this, 0); + + return do_closeArchive(); +} + +//! \internal +Zip::ErrorCode ZipPrivate::do_closeArchive() +{ + // Close current archive by writing out central directory + // and free up resources + + if (!device && !headers) + return Zip::Ok; + + quint32 szCentralDir = 0; + quint32 offCentralDir = device->pos(); + Zip::ErrorCode c = Zip::Ok; + + if (headers && device) { + for (QMap::ConstIterator itr = headers->constBegin(); + itr != headers->constEnd(); ++itr) { + const QString fileName = itr.key(); + const ZipEntryP* h = itr.value(); + c = writeEntry(fileName, h, szCentralDir); + } + } + + if (c == Zip::Ok) + c = writeCentralDir(offCentralDir, szCentralDir); + + if (c != Zip::Ok) { + if (file) { + file->close(); + if (!file->remove()) { + qDebug() << "Failed to delete corrupt archive."; + } + } + } + + return c; +} + +//! \internal +Zip::ErrorCode ZipPrivate::writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir) +{ + unsigned int sz; + + Q_ASSERT(h && device && headers); + + // signature + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x01; + buffer1[3] = 0x02; + + // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported) + buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0; + + // version needed to extract + buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION; + buffer1[ZIP_CD_OFF_VERSION + 1] = 0; + + // general purpose flag + buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0]; + buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1]; + + // compression method + buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF; + buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF; + + // last mod file time + buffer1[ZIP_CD_OFF_MODT] = h->modTime[0]; + buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1]; + + // last mod file date + buffer1[ZIP_CD_OFF_MODD] = h->modDate[0]; + buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1]; + + // crc (4bytes) [16,17,18,19] + setULong(h->crc, buffer1, ZIP_CD_OFF_CRC); + + // compressed size (4bytes: [20,21,22,23]) + setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE); + + // uncompressed size [24,25,26,27] + setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE); + + // filename + QByteArray fileNameBytes = fileName.toLatin1(); + sz = fileNameBytes.size(); + buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF; + buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF; + + // extra field length + buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0; + + // file comment length + buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0; + + // disk number start + buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0; + + // internal file attributes + buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0; + + // external file attributes + buffer1[ZIP_CD_OFF_EATTR] = + buffer1[ZIP_CD_OFF_EATTR + 1] = + buffer1[ZIP_CD_OFF_EATTR + 2] = + buffer1[ZIP_CD_OFF_EATTR + 3] = 0; + + // relative offset of local header [42->45] + setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF); + + if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE) { + return Zip::WriteFailed; + } + + // Write out filename + if ((unsigned int)device->write(fileNameBytes) != sz) { + return Zip::WriteFailed; + } + + szCentralDir += (ZIP_CD_SIZE + sz); + + return Zip::Ok; +} + +//! \internal +Zip::ErrorCode ZipPrivate::writeCentralDir(quint32 offCentralDir, quint32 szCentralDir) +{ + Q_ASSERT(device && headers); + + unsigned int sz; + + // signature + buffer1[0] = 'P'; + buffer1[1] = 'K'; + buffer1[2] = 0x05; + buffer1[3] = 0x06; + + // number of this disk + buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0; + + // number of disk with central directory + buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0; + + // number of entries in this disk + sz = headers->count(); + buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF; + buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF; + + // total number of entries + buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES]; + buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1]; + + // size of central directory [12->15] + setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE); + + // central dir offset [16->19] + setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF); + + // ZIP file comment length + QByteArray commentBytes = comment.toLatin1(); + quint16 commentLength = commentBytes.size(); + + if (commentLength == 0) { + buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0; + } else { + buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF; + buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF; + } + + if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE) { + return Zip::WriteFailed; + } + + if (commentLength != 0) { + if ((unsigned int)device->write(commentBytes) != commentLength) { + return Zip::WriteFailed; + } + } + + return Zip::Ok; +} + +//! \internal +void ZipPrivate::reset() +{ + comment.clear(); + + if (headers) { + qDeleteAll(*headers); + delete headers; + headers = 0; + } + + device = 0; + + if (file) + delete file; + file = 0; +} + +//! \internal Returns the path of the parent directory +QString ZipPrivate::extractRoot(const QString& p, Zip::CompressionOptions o) +{ + Q_UNUSED(o); + QDir d(QDir::cleanPath(p)); + if (!d.exists()) + return QString(); + + if (!d.cdUp()) + return QString(); + + return d.absolutePath(); +} + + +/************************************************************************ + Public interface +*************************************************************************/ + +/*! + Creates a new Zip file compressor. +*/ +Zip::Zip() : d(new ZipPrivate) +{ +} + +/*! + Closes any open archive and releases used resources. +*/ +Zip::~Zip() +{ + closeArchive(); + delete d; +} + +/*! + Returns true if there is an open archive. +*/ +bool Zip::isOpen() const +{ + return d->device; +} + +/*! + Sets the password to be used for the next files being added! + Files added before calling this method will use the previously + set password (if any). + Closing the archive won't clear the password! +*/ +void Zip::setPassword(const QString& pwd) +{ + d->password = pwd; +} + +//! Convenience method, clears the current password. +void Zip::clearPassword() +{ + d->password.clear(); +} + +//! Returns the currently used password. +QString Zip::password() const +{ + return d->password; +} + +/*! + Attempts to create a new Zip archive. If \p overwrite is true and the file + already exist it will be overwritten. + Any open archive will be closed. + */ +Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite) +{ + closeArchive(); + Q_ASSERT(!d->device && !d->file); + + if (filename.isEmpty()) + return Zip::FileNotFound; + + d->file = new QFile(filename); + + if (d->file->exists() && !overwrite) { + delete d->file; + d->file = 0; + return Zip::FileExists; + } + + if (!d->file->open(QIODevice::WriteOnly)) { + delete d->file; + d->file = 0; + return Zip::OpenFailed; + } + + const Zip::ErrorCode ec = createArchive(d->file); + if (ec != Zip::Ok) { + closeArchive(); + } + + return ec; +} + +/*! + Attempts to create a new Zip archive. If there is another open archive this will be closed. + \warning The class takes ownership of the device! + */ +Zip::ErrorCode Zip::createArchive(QIODevice* device) +{ + if (!device) { + qDebug() << "Invalid device."; + return Zip::OpenFailed; + } + + return d->createArchive(device); +} + +/*! + Returns the current archive comment. +*/ +QString Zip::archiveComment() const +{ + return d->comment; +} + +/*! + Sets the comment for this archive. Note: createArchive() should have been + called before. +*/ +void Zip::setArchiveComment(const QString& comment) +{ + d->comment = comment; +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::IgnorePaths flag as compression option and an empty \p root parameter. + + The result is that all files found in \p path (and in subdirectories) are + added to the zip file without a directory entry. +*/ +Zip::ErrorCode Zip::addDirectoryContents(const QString& path, CompressionLevel level) +{ + return addDirectory(path, QString(), IgnorePaths, level); +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::IgnorePaths flag as compression option. + + The result is that all files found in \p path (and in subdirectories) are + added to the zip file without a directory entry (or within a directory + structure specified by \p root). +*/ +Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level) +{ + return addDirectory(path, root, IgnorePaths, level); +} + +/*! + Convenience method, same as calling + Zip::addDirectory(const QString&,const QString&,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionLevel level) +{ + return addDirectory(path, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level) +{ + return addDirectory(path, root, Zip::RelativePaths, level); +} + +/*! + Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder. + Stops adding files if some error occurs. + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). + + If \p addedFiles is not null it is set to the number of successfully added + files. +*/ +Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level, int* addedFiles) +{ + const int hierarchyLev = 0; + return d->addDirectory(path, root, options, level, hierarchyLev, addedFiles); +} + +/*! + Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths as compression option. + */ +Zip::ErrorCode Zip::addFile(const QString& path, CompressionLevel level) +{ + return addFile(path, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addFile(const QString&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, + CompressionLevel level) +{ + return addFile(path, root, Zip::RelativePaths, level); +} + +/*! + Adds the file at \p path to the archive, using \p root as name for the root folder. + If \p path points to a directory the behaviour is basically the same as + addDirectory(). + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). +*/ +Zip::ErrorCode Zip::addFile(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level) +{ + if (path.isEmpty()) + return Zip::Ok; + return addFiles(QStringList() << path, root, options, level); +} + +/*! + Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) + with an empty \p root parameter and Zip::RelativePaths as compression option. + */ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, CompressionLevel level) +{ + return addFiles(paths, QString(), Zip::RelativePaths, level); +} + +/*! + Convenience method, same as calling Zip::addFiles(const QStringList&,const QString&,CompressionOptions,CompressionLevel) + with the Zip::RelativePaths flag as compression option. + */ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, + CompressionLevel level) +{ + return addFiles(paths, root, Zip::RelativePaths, level); +} + +/*! + Adds the files or directories in \p paths to the archive, using \p root as + name for the root folder. + This is similar to calling addFile or addDirectory for all the entries in + \p paths, except it is slightly faster. + + The ExtractionOptions are checked in the order they are defined in the zip.h heaser file. + This means that the last one overwrites the previous one (if some conflict occurs), i.e. + Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths. + + The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing / + is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!). + + If \p addedFiles is not null it is set to the number of successfully added + files. +*/ +Zip::ErrorCode Zip::addFiles(const QStringList& paths, const QString& root, + CompressionOptions options, CompressionLevel level, int* addedFiles) +{ + return d->addFiles(paths, root, options, level, addedFiles); +} + +/*! + Closes the archive and writes any pending data. +*/ +Zip::ErrorCode Zip::closeArchive() +{ + Zip::ErrorCode ec = d->closeArchive(); + d->reset(); + return ec; +} + +/*! + Returns a locale translated error string for a given error code. +*/ +QString Zip::formatError(Zip::ErrorCode c) const +{ + switch (c) + { + case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break; + case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break; + case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break; + case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break; + case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break; + case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break; + case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break; + case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break; + case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break; + default: ; + } + + return QCoreApplication::translate("Zip", "Unknown error."); +} + +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/zip.h b/oracle/src/zip/zip.h index 79f32ada5..79c512ce4 100755 --- a/oracle/src/zip/zip.h +++ b/oracle/src/zip/zip.h @@ -1,158 +1,158 @@ -/**************************************************************************** -** Filename: zip.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_ZIP__H -#define OSDAB_ZIP__H - -#include "zipglobal.h" - -#include -#include - -#include - -class QIODevice; -class QFile; -class QDir; -class QStringList; -class QString; - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipPrivate; - -class OSDAB_ZIP_EXPORT Zip -{ -public: - enum ErrorCode - { - Ok, - ZlibInit, - ZlibError, - FileExists, - OpenFailed, - NoOpenArchive, - FileNotFound, - ReadFailed, - WriteFailed, - SeekFailed, - InternalError - }; - - enum CompressionLevel - { - Store, - Deflate1 = 1, Deflate2, Deflate3, Deflate4, - Deflate5, Deflate6, Deflate7, Deflate8, Deflate9, - AutoCPU, AutoMIME, AutoFull - }; - - enum CompressionOption - { - /*! Does not preserve absolute paths in the zip file when adding a - file or directory (default) */ - RelativePaths = 0x0001, - /*! Preserve absolute paths */ - AbsolutePaths = 0x0002, - /*! Do not store paths. All the files are put in the (evtl. user defined) - root of the zip file */ - IgnorePaths = 0x0004, - /*! Works only with addDirectory(). Adds the directory's contents, - including subdirectories, but does not add an entry for the root - directory itself. */ - IgnoreRoot = 0x0008, - /*! Used only when compressing a directory or multiple files. - If set invalid or unreadable files are simply skipped. - */ - SkipBadFiles = 0x0020, - /*! Makes sure a file is never added twice to the same zip archive. - This check is only necessary in certain usage scenarios and given - that it slows down processing you need to enable it explicitly with - this flag. - */ - CheckForDuplicates = 0x0040 - }; - Q_DECLARE_FLAGS(CompressionOptions, CompressionOption) - - Zip(); - virtual ~Zip(); - - bool isOpen() const; - - void setPassword(const QString& pwd); - void clearPassword(); - QString password() const; - - ErrorCode createArchive(const QString& file, bool overwrite = true); - ErrorCode createArchive(QIODevice* device); - - QString archiveComment() const; - void setArchiveComment(const QString& comment); - - ErrorCode addDirectoryContents(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addDirectoryContents(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - - ErrorCode addDirectory(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addDirectory(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addDirectory(const QString& path, const QString& root, - CompressionOptions options, CompressionLevel level = AutoFull, - int* addedFiles = 0); - - ErrorCode addFile(const QString& path, - CompressionLevel level = AutoFull); - ErrorCode addFile(const QString& path, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addFile(const QString& path, const QString& root, - CompressionOptions options, - CompressionLevel level = AutoFull); - - ErrorCode addFiles(const QStringList& paths, - CompressionLevel level = AutoFull); - ErrorCode addFiles(const QStringList& paths, const QString& root, - CompressionLevel level = AutoFull); - ErrorCode addFiles(const QStringList& paths, const QString& root, - CompressionOptions options, - CompressionLevel level = AutoFull, - int* addedFiles = 0); - - ErrorCode closeArchive(); - - QString formatError(ErrorCode c) const; - -private: - ZipPrivate* d; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(Zip::CompressionOptions) - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIP__H +/**************************************************************************** +** Filename: zip.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_ZIP__H +#define OSDAB_ZIP__H + +#include "zipglobal.h" + +#include +#include + +#include + +class QIODevice; +class QFile; +class QDir; +class QStringList; +class QString; + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipPrivate; + +class OSDAB_ZIP_EXPORT Zip +{ +public: + enum ErrorCode + { + Ok, + ZlibInit, + ZlibError, + FileExists, + OpenFailed, + NoOpenArchive, + FileNotFound, + ReadFailed, + WriteFailed, + SeekFailed, + InternalError + }; + + enum CompressionLevel + { + Store, + Deflate1 = 1, Deflate2, Deflate3, Deflate4, + Deflate5, Deflate6, Deflate7, Deflate8, Deflate9, + AutoCPU, AutoMIME, AutoFull + }; + + enum CompressionOption + { + /*! Does not preserve absolute paths in the zip file when adding a + file or directory (default) */ + RelativePaths = 0x0001, + /*! Preserve absolute paths */ + AbsolutePaths = 0x0002, + /*! Do not store paths. All the files are put in the (evtl. user defined) + root of the zip file */ + IgnorePaths = 0x0004, + /*! Works only with addDirectory(). Adds the directory's contents, + including subdirectories, but does not add an entry for the root + directory itself. */ + IgnoreRoot = 0x0008, + /*! Used only when compressing a directory or multiple files. + If set invalid or unreadable files are simply skipped. + */ + SkipBadFiles = 0x0020, + /*! Makes sure a file is never added twice to the same zip archive. + This check is only necessary in certain usage scenarios and given + that it slows down processing you need to enable it explicitly with + this flag. + */ + CheckForDuplicates = 0x0040 + }; + Q_DECLARE_FLAGS(CompressionOptions, CompressionOption) + + Zip(); + virtual ~Zip(); + + bool isOpen() const; + + void setPassword(const QString& pwd); + void clearPassword(); + QString password() const; + + ErrorCode createArchive(const QString& file, bool overwrite = true); + ErrorCode createArchive(QIODevice* device); + + QString archiveComment() const; + void setArchiveComment(const QString& comment); + + ErrorCode addDirectoryContents(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addDirectoryContents(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + + ErrorCode addDirectory(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addDirectory(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addDirectory(const QString& path, const QString& root, + CompressionOptions options, CompressionLevel level = AutoFull, + int* addedFiles = 0); + + ErrorCode addFile(const QString& path, + CompressionLevel level = AutoFull); + ErrorCode addFile(const QString& path, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addFile(const QString& path, const QString& root, + CompressionOptions options, + CompressionLevel level = AutoFull); + + ErrorCode addFiles(const QStringList& paths, + CompressionLevel level = AutoFull); + ErrorCode addFiles(const QStringList& paths, const QString& root, + CompressionLevel level = AutoFull); + ErrorCode addFiles(const QStringList& paths, const QString& root, + CompressionOptions options, + CompressionLevel level = AutoFull, + int* addedFiles = 0); + + ErrorCode closeArchive(); + + QString formatError(ErrorCode c) const; + +private: + ZipPrivate* d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Zip::CompressionOptions) + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIP__H diff --git a/oracle/src/zip/zip_p.h b/oracle/src/zip/zip_p.h index b8ec6564b..81c2dacd0 100755 --- a/oracle/src/zip/zip_p.h +++ b/oracle/src/zip/zip_p.h @@ -1,133 +1,133 @@ -/**************************************************************************** -** Filename: zip_p.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_ZIP_P__H -#define OSDAB_ZIP_P__H - -#include "zip.h" -#include "zipentry_p.h" - -#include -#include -#include - -#include - -/*! - zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) - we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) -*/ -#define ZIP_READ_BUFFER (256*1024) - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipPrivate : public QObject -{ - Q_OBJECT - -public: - // uLongf from zconf.h - typedef uLongf crc_t; - - ZipPrivate(); - virtual ~ZipPrivate(); - - QMap* headers; - - QIODevice* device; - QFile* file; - - char buffer1[ZIP_READ_BUFFER]; - char buffer2[ZIP_READ_BUFFER]; - - unsigned char* uBuffer; - - const crc_t* crcTable; - - QString comment; - QString password; - - Zip::ErrorCode createArchive(QIODevice* device); - Zip::ErrorCode closeArchive(); - void reset(); - - bool zLibInit(); - - bool containsEntry(const QFileInfo& info) const; - - Zip::ErrorCode addDirectory(const QString& path, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int hierarchyLevel, int* addedFiles = 0); - Zip::ErrorCode addFiles(const QStringList& paths, const QString& root, - Zip::CompressionOptions options, Zip::CompressionLevel level, - int* addedFiles); - - Zip::ErrorCode createEntry(const QFileInfo& file, const QString& root, - Zip::CompressionLevel level); - Zip::CompressionLevel detectCompressionByMime(const QString& ext); - - inline quint32 updateChecksum(const quint32& crc, const quint32& val) const; - - inline void encryptBytes(quint32* keys, char* buffer, qint64 read); - - inline void setULong(quint32 v, char* buffer, unsigned int offset); - inline void updateKeys(quint32* keys, int c) const; - inline void initKeys(quint32* keys) const; - inline int decryptByte(quint32 key2) const; - - inline QString extractRoot(const QString& p, Zip::CompressionOptions o); - -private slots: - void deviceDestroyed(QObject*); - -private: - int compressionStrategy(const QString& path, QIODevice& file) const; - Zip::ErrorCode deflateFile(const QFileInfo& fileInfo, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); - Zip::ErrorCode storeFile(const QString& path, QIODevice& file, - quint32& crc, qint64& written, quint32** keys); - Zip::ErrorCode compressFile(const QString& path, QIODevice& file, - quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); - Zip::ErrorCode do_closeArchive(); - Zip::ErrorCode writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir); - Zip::ErrorCode writeCentralDir(quint32 offCentralDir, quint32 szCentralDir); -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIP_P__H +/**************************************************************************** +** Filename: zip_p.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_ZIP_P__H +#define OSDAB_ZIP_P__H + +#include "zip.h" +#include "zipentry_p.h" + +#include +#include +#include + +#include + +/*! + zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate()) + we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;) +*/ +#define ZIP_READ_BUFFER (256*1024) + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipPrivate : public QObject +{ + Q_OBJECT + +public: + // uLongf from zconf.h + typedef uLongf crc_t; + + ZipPrivate(); + virtual ~ZipPrivate(); + + QMap* headers; + + QIODevice* device; + QFile* file; + + char buffer1[ZIP_READ_BUFFER]; + char buffer2[ZIP_READ_BUFFER]; + + unsigned char* uBuffer; + + const crc_t* crcTable; + + QString comment; + QString password; + + Zip::ErrorCode createArchive(QIODevice* device); + Zip::ErrorCode closeArchive(); + void reset(); + + bool zLibInit(); + + bool containsEntry(const QFileInfo& info) const; + + Zip::ErrorCode addDirectory(const QString& path, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int hierarchyLevel, int* addedFiles = 0); + Zip::ErrorCode addFiles(const QStringList& paths, const QString& root, + Zip::CompressionOptions options, Zip::CompressionLevel level, + int* addedFiles); + + Zip::ErrorCode createEntry(const QFileInfo& file, const QString& root, + Zip::CompressionLevel level); + Zip::CompressionLevel detectCompressionByMime(const QString& ext); + + inline quint32 updateChecksum(const quint32& crc, const quint32& val) const; + + inline void encryptBytes(quint32* keys, char* buffer, qint64 read); + + inline void setULong(quint32 v, char* buffer, unsigned int offset); + inline void updateKeys(quint32* keys, int c) const; + inline void initKeys(quint32* keys) const; + inline int decryptByte(quint32 key2) const; + + inline QString extractRoot(const QString& p, Zip::CompressionOptions o); + +private slots: + void deviceDestroyed(QObject*); + +private: + int compressionStrategy(const QString& path, QIODevice& file) const; + Zip::ErrorCode deflateFile(const QFileInfo& fileInfo, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); + Zip::ErrorCode storeFile(const QString& path, QIODevice& file, + quint32& crc, qint64& written, quint32** keys); + Zip::ErrorCode compressFile(const QString& path, QIODevice& file, + quint32& crc, qint64& written, const Zip::CompressionLevel& level, quint32** keys); + Zip::ErrorCode do_closeArchive(); + Zip::ErrorCode writeEntry(const QString& fileName, const ZipEntryP* h, quint32& szCentralDir); + Zip::ErrorCode writeCentralDir(quint32 offCentralDir, quint32 szCentralDir); +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIP_P__H diff --git a/oracle/src/zip/zipentry_p.h b/oracle/src/zip/zipentry_p.h index f0675b6b6..5c355f590 100755 --- a/oracle/src/zip/zipentry_p.h +++ b/oracle/src/zip/zipentry_p.h @@ -1,91 +1,91 @@ -/**************************************************************************** -** Filename: ZipEntryP.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** Wrapper for a ZIP local header. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Zip/UnZip API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef OSDAB_ZIPENTRY_P__H -#define OSDAB_ZIPENTRY_P__H - -#include -#include - -OSDAB_BEGIN_NAMESPACE(Zip) - -class ZipEntryP -{ -public: - ZipEntryP() : - lhOffset(0), - dataOffset(0), - gpFlag(), - compMethod(0), - modTime(), - modDate(), - crc(0), - szComp(0), - szUncomp(0), - absolutePath(), - fileSize(0), - lhEntryChecked(false) - { - gpFlag[0] = gpFlag[1] = 0; - modTime[0] = modTime[1] = 0; - modDate[0] = modDate[1] = 0; - } - - quint32 lhOffset; // Offset of the local header record for this entry - mutable quint32 dataOffset; // Offset of the file data for this entry - unsigned char gpFlag[2]; // General purpose flag - quint16 compMethod; // Compression method - unsigned char modTime[2]; // Last modified time - unsigned char modDate[2]; // Last modified date - quint32 crc; // CRC32 - quint32 szComp; // Compressed file size - quint32 szUncomp; // Uncompressed file size - QString comment; // File comment - - QString absolutePath; // Internal use - qint64 fileSize; // Internal use - - mutable bool lhEntryChecked; // Is true if the local header record for this entry has been parsed - - inline bool isEncrypted() const { return gpFlag[0] & 0x01; } - inline bool hasDataDescriptor() const { return gpFlag[0] & 0x08; } -}; - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIPENTRY_P__H +/**************************************************************************** +** Filename: ZipEntryP.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** Wrapper for a ZIP local header. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Zip/UnZip API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef OSDAB_ZIPENTRY_P__H +#define OSDAB_ZIPENTRY_P__H + +#include +#include + +OSDAB_BEGIN_NAMESPACE(Zip) + +class ZipEntryP +{ +public: + ZipEntryP() : + lhOffset(0), + dataOffset(0), + gpFlag(), + compMethod(0), + modTime(), + modDate(), + crc(0), + szComp(0), + szUncomp(0), + absolutePath(), + fileSize(0), + lhEntryChecked(false) + { + gpFlag[0] = gpFlag[1] = 0; + modTime[0] = modTime[1] = 0; + modDate[0] = modDate[1] = 0; + } + + quint32 lhOffset; // Offset of the local header record for this entry + mutable quint32 dataOffset; // Offset of the file data for this entry + unsigned char gpFlag[2]; // General purpose flag + quint16 compMethod; // Compression method + unsigned char modTime[2]; // Last modified time + unsigned char modDate[2]; // Last modified date + quint32 crc; // CRC32 + quint32 szComp; // Compressed file size + quint32 szUncomp; // Uncompressed file size + QString comment; // File comment + + QString absolutePath; // Internal use + qint64 fileSize; // Internal use + + mutable bool lhEntryChecked; // Is true if the local header record for this entry has been parsed + + inline bool isEncrypted() const { return gpFlag[0] & 0x01; } + inline bool hasDataDescriptor() const { return gpFlag[0] & 0x08; } +}; + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIPENTRY_P__H diff --git a/oracle/src/zip/zipglobal.cpp b/oracle/src/zip/zipglobal.cpp old mode 100644 new mode 100755 index e972005f5..5abf6a723 --- a/oracle/src/zip/zipglobal.cpp +++ b/oracle/src/zip/zipglobal.cpp @@ -1,150 +1,152 @@ -/**************************************************************************** -** Filename: zipglobal.cpp -** Last updated [dd/mm/yyyy]: 06/02/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#include "zipglobal.h" - -#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined(Q_OS_MACOS) -#define OSDAB_ZIP_HAS_UTC -#include -#else -#undef OSDAB_ZIP_HAS_UTC -#endif - -#if defined(Q_OS_WIN) -#include -#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS) -#include -#endif - -OSDAB_BEGIN_NAMESPACE(Zip) - -/*! Returns the current UTC offset in seconds unless OSDAB_ZIP_NO_UTC is defined - and method is implemented for the current platform and 0 otherwise. -*/ -int OSDAB_ZIP_MANGLE(currentUtcOffset)() -{ -#if !(!defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC) - return 0; -#else - time_t curr_time_t; - time(&curr_time_t); - -#if defined Q_OS_WIN - struct tm _tm_struct; - struct tm *tm_struct = &_tm_struct; -#else - struct tm *tm_struct = 0; -#endif - -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) - // use the reentrant version of localtime() where available - tzset(); - tm res; - tm_struct = gmtime_r(&curr_time_t, &res); -#elif defined Q_OS_WIN && !defined Q_CC_MINGW - if (gmtime_s(tm_struct, &curr_time_t)) - return 0; -#else - tm_struct = gmtime(&curr_time_t); -#endif - - if (!tm_struct) - return 0; - - const time_t global_time_t = mktime(tm_struct); - -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) - // use the reentrant version of localtime() where available - tm_struct = localtime_r(&curr_time_t, &res); -#elif defined Q_OS_WIN && !defined Q_CC_MINGW - if (localtime_s(tm_struct, &curr_time_t)) - return 0; -#else - tm_struct = localtime(&curr_time_t); -#endif - - if (!tm_struct) - return 0; - - const time_t local_time_t = mktime(tm_struct); - - const int utcOffset = -qRound(difftime(global_time_t, local_time_t)); - return tm_struct->tm_isdst > 0 ? utcOffset + 3600 : utcOffset; -#endif // No UTC -} - -QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime &dateTime) -{ -#if !defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC - const int utc = OSDAB_ZIP_MANGLE(currentUtcOffset)(); - return dateTime.toUTC().addSecs(utc); -#else - return dateTime; -#endif // OSDAB_ZIP_NO_UTC -} - -bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString &fileName, const QDateTime &dateTime) -{ - if (fileName.isEmpty()) - return true; - -#ifdef Q_OS_WIN - HANDLE hFile = - CreateFileW(fileName.toStdWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); - if (hFile == INVALID_HANDLE_VALUE) { - return false; - } - - SYSTEMTIME st; - FILETIME ft, ftLastMod; - const QDate date = dateTime.date(); - const QTime time = dateTime.time(); - st.wYear = date.year(); - st.wMonth = date.month(); - st.wDay = date.day(); - st.wHour = time.hour(); - st.wMinute = time.minute(); - st.wSecond = time.second(); - st.wMilliseconds = time.msec(); - - SystemTimeToFileTime(&st, &ft); - LocalFileTimeToFileTime(&ft, &ftLastMod); - - const bool success = SetFileTime(hFile, NULL, NULL, &ftLastMod); - CloseHandle(hFile); - return success; - -#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS) - - struct utimbuf t_buffer; - t_buffer.actime = t_buffer.modtime = dateTime.toSecsSinceEpoch(); - return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0; -#endif - - return true; -} -OSDAB_END_NAMESPACE +/**************************************************************************** +** Filename: zipglobal.cpp +** Last updated [dd/mm/yyyy]: 06/02/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#include "zipglobal.h" + +#if defined(Q_OS_WIN) || defined(Q_OS_WINCE) || defined(Q_OS_LINUX) || defined (Q_OS_MACX) +#define OSDAB_ZIP_HAS_UTC +#include +#else +#undef OSDAB_ZIP_HAS_UTC +#endif + +#if defined(Q_OS_WIN) +#include +#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) +#include +#endif + +OSDAB_BEGIN_NAMESPACE(Zip) + +/*! Returns the current UTC offset in seconds unless OSDAB_ZIP_NO_UTC is defined + and method is implemented for the current platform and 0 otherwise. +*/ +int OSDAB_ZIP_MANGLE(currentUtcOffset)() +{ +#if !(!defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC) + return 0; +#else + time_t curr_time_t; + time(&curr_time_t); + +#if defined Q_OS_WIN + struct tm _tm_struct; + struct tm* tm_struct = &_tm_struct; +#else + struct tm* tm_struct = 0; +#endif + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tzset(); + tm res; + tm_struct = gmtime_r(&curr_time_t, &res); +#elif defined Q_OS_WIN && !defined Q_CC_MINGW + if (gmtime_s(tm_struct, &curr_time_t)) + return 0; +#else + tm_struct = gmtime(&curr_time_t); +#endif + + if (!tm_struct) + return 0; + + const time_t global_time_t = mktime(tm_struct); + +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) + // use the reentrant version of localtime() where available + tm_struct = localtime_r(&curr_time_t, &res); +#elif defined Q_OS_WIN && !defined Q_CC_MINGW + if (localtime_s(tm_struct, &curr_time_t)) + return 0; +#else + tm_struct = localtime(&curr_time_t); +#endif + + if (!tm_struct) + return 0; + + const time_t local_time_t = mktime(tm_struct); + + const int utcOffset = - qRound(difftime(global_time_t, local_time_t)); + return tm_struct->tm_isdst > 0 ? utcOffset + 3600 : utcOffset; +#endif // No UTC + + return 0; +} + +QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime) +{ +#if !defined OSDAB_ZIP_NO_UTC && defined OSDAB_ZIP_HAS_UTC + const int utc = OSDAB_ZIP_MANGLE(currentUtcOffset)(); + return dateTime.toUTC().addSecs(utc); +#else + return dateTime; +#endif // OSDAB_ZIP_NO_UTC +} + +bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime) +{ + if (fileName.isEmpty()) + return true; + +#ifdef Q_OS_WIN + HANDLE hFile = CreateFileW(fileName.toStdWString().c_str(), + GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + + SYSTEMTIME st; + FILETIME ft, ftLastMod; + const QDate date = dateTime.date(); + const QTime time = dateTime.time(); + st.wYear = date.year(); + st.wMonth = date.month(); + st.wDay = date.day(); + st.wHour = time.hour(); + st.wMinute = time.minute(); + st.wSecond = time.second(); + st.wMilliseconds = time.msec(); + + SystemTimeToFileTime(&st, &ft); + LocalFileTimeToFileTime(&ft, &ftLastMod); + + const bool success = SetFileTime(hFile, NULL, NULL, &ftLastMod); + CloseHandle(hFile); + return success; + +#elif defined(Q_OS_LINUX) || defined(Q_OS_MACX) + + struct utimbuf t_buffer; + t_buffer.actime = t_buffer.modtime = dateTime.toTime_t(); + return utime(fileName.toLocal8Bit().constData(), &t_buffer) == 0; +#endif + + return true; +} +OSDAB_END_NAMESPACE diff --git a/oracle/src/zip/zipglobal.h b/oracle/src/zip/zipglobal.h index e7ff33105..0b4c94f66 100755 --- a/oracle/src/zip/zipglobal.h +++ b/oracle/src/zip/zipglobal.h @@ -1,77 +1,77 @@ -/**************************************************************************** -** Filename: zipglobal.h -** Last updated [dd/mm/yyyy]: 27/03/2011 -** -** pkzip 2.0 file compression. -** -** Some of the code has been inspired by other open source projects, -** (mainly Info-Zip and Gilles Vollant's minizip). -** Compression and decompression actually uses the zlib library. -** -** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. -** -** This file is part of the OSDaB project (http://osdab.42cows.org/). -** -** This file may be distributed and/or modified under the terms of the -** GNU General Public License version 2 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. -** -** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE -** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. -** -** See the file LICENSE.GPL that came with this software distribution or -** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. -** -**********************************************************************/ - -#ifndef OSDAB_ZIPGLOBAL__H -#define OSDAB_ZIPGLOBAL__H - -#include -#include - -/* If you want to build the OSDaB Zip code as - a library, define OSDAB_ZIP_LIB in the library's .pro file and - in the libraries using it OR remove the #ifndef OSDAB_ZIP_LIB - define below and leave the #else body. Also remember to define - OSDAB_ZIP_BUILD_LIB in the library's project). -*/ - -#ifndef OSDAB_ZIP_LIB -# define OSDAB_ZIP_EXPORT -#else -# if defined(OSDAB_ZIP_BUILD_LIB) -# define OSDAB_ZIP_EXPORT Q_DECL_EXPORT -# else -# define OSDAB_ZIP_EXPORT Q_DECL_IMPORT -# endif -#endif - -#ifdef OSDAB_NAMESPACE -#define OSDAB_BEGIN_NAMESPACE(ModuleName) namespace Osdab { namespace ModuleName { -#else -#define OSDAB_BEGIN_NAMESPACE(ModuleName) -#endif - -#ifdef OSDAB_NAMESPACE -#define OSDAB_END_NAMESPACE } } -#else -#define OSDAB_END_NAMESPACE -#endif - -#ifndef OSDAB_NAMESPACE -#define OSDAB_ZIP_MANGLE(x) zip_##x -#else -#define OSDAB_ZIP_MANGLE(x) x -#endif - -OSDAB_BEGIN_NAMESPACE(Zip) - -OSDAB_ZIP_EXPORT int OSDAB_ZIP_MANGLE(currentUtcOffset)(); -OSDAB_ZIP_EXPORT QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime); -OSDAB_ZIP_EXPORT bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime); - -OSDAB_END_NAMESPACE - -#endif // OSDAB_ZIPGLOBAL__H +/**************************************************************************** +** Filename: zipglobal.h +** Last updated [dd/mm/yyyy]: 27/03/2011 +** +** pkzip 2.0 file compression. +** +** Some of the code has been inspired by other open source projects, +** (mainly Info-Zip and Gilles Vollant's minizip). +** Compression and decompression actually uses the zlib library. +** +** Copyright (C) 2007-2012 Angius Fabrizio. All rights reserved. +** +** This file is part of the OSDaB project (http://osdab.42cows.org/). +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See the file LICENSE.GPL that came with this software distribution or +** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information. +** +**********************************************************************/ + +#ifndef OSDAB_ZIPGLOBAL__H +#define OSDAB_ZIPGLOBAL__H + +#include +#include + +/* If you want to build the OSDaB Zip code as + a library, define OSDAB_ZIP_LIB in the library's .pro file and + in the libraries using it OR remove the #ifndef OSDAB_ZIP_LIB + define below and leave the #else body. Also remember to define + OSDAB_ZIP_BUILD_LIB in the library's project). +*/ + +#ifndef OSDAB_ZIP_LIB +# define OSDAB_ZIP_EXPORT +#else +# if defined(OSDAB_ZIP_BUILD_LIB) +# define OSDAB_ZIP_EXPORT Q_DECL_EXPORT +# else +# define OSDAB_ZIP_EXPORT Q_DECL_IMPORT +# endif +#endif + +#ifdef OSDAB_NAMESPACE +#define OSDAB_BEGIN_NAMESPACE(ModuleName) namespace Osdab { namespace ModuleName { +#else +#define OSDAB_BEGIN_NAMESPACE(ModuleName) +#endif + +#ifdef OSDAB_NAMESPACE +#define OSDAB_END_NAMESPACE } } +#else +#define OSDAB_END_NAMESPACE +#endif + +#ifndef OSDAB_NAMESPACE +#define OSDAB_ZIP_MANGLE(x) zip_##x +#else +#define OSDAB_ZIP_MANGLE(x) x +#endif + +OSDAB_BEGIN_NAMESPACE(Zip) + +OSDAB_ZIP_EXPORT int OSDAB_ZIP_MANGLE(currentUtcOffset)(); +OSDAB_ZIP_EXPORT QDateTime OSDAB_ZIP_MANGLE(fromFileTimestamp)(const QDateTime& dateTime); +OSDAB_ZIP_EXPORT bool OSDAB_ZIP_MANGLE(setFileTimestamp)(const QString& fileName, const QDateTime& dateTime); + +OSDAB_END_NAMESPACE + +#endif // OSDAB_ZIPGLOBAL__H diff --git a/oracle/translations/oracle_cs.ts b/oracle/translations/oracle_cs.ts index 1578b52cb..583c42eb2 100644 --- a/oracle/translations/oracle_cs.ts +++ b/oracle/translations/oracle_cs.ts @@ -1,251 +1,161 @@ - + IntroPage - Introduction - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. + English + Česky (Czech) + + + Language: - - Interface language: - - - - - Version: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. LoadSetsPage - Source selection - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - - - - Local file: - - Restore default URL - - - - Choose file... - Load sets file - - Sets JSON file (%1) + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) - - - - - - - Error - Chyba - - - - The provided URL is not valid. - Downloading (0MB) - Please choose a file. - Cannot open file '%1'. - Downloading (%1MB) - Network error: %1. - Parsing file - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip extraction failed: %1. - Sorry, this version of Oracle does not support zipped files. - - Failed to interpret downloaded data. + Do you want to try to download a fresh copy of the uncompressed file instead? - - Do you want to download the uncompressed file instead? - - - - The file was retrieved successfully, but it does not contain any sets data. - - - LoadSpoilersPage - - Save spoiler database + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - XML; spoiler database (*.xml) - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - Restore default URL - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error - - Tokens import + Downloading (0MB) - - Please specify a compatible source for token data. + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - Download URL: - Restore default URL - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens @@ -253,139 +163,102 @@ OracleWizard - Oracle Importer - - - OutroPage - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. + Save SaveSetsPage - - - Error - Chyba - - - - No set has been imported. - - - - Sets imported - - A cockatrice database file of %1 MB has been downloaded. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - The following sets have been found: + Save to the default path (recommended) - - Press "Save" to store the imported cards in the Cockatrice database. + Error - - The card database will be saved at the following location: + No set has been imported. - - Save to a custom path (not recommended) - - - - - &Save - - - - Import finished: %1 cards. - %1: %2 cards imported - Save card database - XML; card database (*.xml) - + Success + + + + The card database has been saved successfully to +%1 + + + The file could not be saved to %1 - SimpleDownloadFilePage + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + - - - Error - - The provided URL is not valid. - - - - - Downloading (0MB) - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 @@ -393,87 +266,70 @@ UnZip - ZIP operation completed successfully. - Failed to initialize or load zlib library. - zlib library error. - Unable to create or open file. - Partially corrupted archive. Some files might be extracted. - Corrupted archive. - Wrong password. - No archive has been created yet. - File or directory does not exist. - File read error. - File write error. - File seek error. - Unable to create a directory. - Invalid device. - Invalid or incompatible zip archive. - Inconsistent headers. Archive might be corrupted. - Unknown error. @@ -481,70 +337,44 @@ Zip - ZIP operation completed successfully. - Failed to initialize or load zlib library. - zlib library error. - Unable to create or open file. - No archive has been created yet. - File or directory does not exist. - File read error. - File write error. - File seek error. - Unknown error. - - i18n - - - English - Česky (Czech) - - - - main - - - Only run in spoiler mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_de.ts b/oracle/translations/oracle_de.ts index df12f40f4..bdf28d7ec 100644 --- a/oracle/translations/oracle_de.ts +++ b/oracle/translations/oracle_de.ts @@ -1,282 +1,162 @@ - + IntroPage - Introduction Einführung - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Dieser Assistent wird eine Liste aller Editionen, Karten und Spielsteine importieren, die von Cockatrice genutzt werden. + English + Deutsch (German) - - Interface language: - Interfacesprache: + Language: + Sprache: - - Version: - Version: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + Dieser Assistent wird eine Liste aller Editionen, Karten und Spielsteine, die von Cockatrice genutzt werden, importieren. +Sie müssen eine URL oder einen Dateinamen als Quelle angeben. LoadSetsPage - Source selection Quellenauswahl - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Bitte geben Sie eine kompatible Quelle für die Liste der Editionen und Karten an. Sie können eine URL-Adresse zum Herunterladen angeben oder eine existierende Datei von Ihrem Computer verwenden. - - - - Download URL: - Download URL: - - - Local file: Lokale Datei: - - Restore default URL - Standard-URL wiederherstellen - - - Choose file... Datei auswählen... - Load sets file Editionsdatei wird geladen - - Sets file (%1) - Sets JSON file (%1) - Sets Datei (%1) + Sets JSON file (*.json *.zip) + JSON Editionsdatei (*.json *.zip) + + + Sets JSON file (*.json) + JSON Editionsdatei (*.json) - - - - - - - Error Fehler - - The provided URL is not valid. - Die eingegebene URL ist nicht gültig. - - - Downloading (0MB) Herunterladen (0MB) - Please choose a file. - Bitte wähle eine Datei aus. + Bitte wählen Sie eine Datei. - Cannot open file '%1'. Datei '%1' kann nicht geöffnet werden. - Downloading (%1MB) Herunterladen (%1MB) - Network error: %1. Netzwerkfehler: %1. - Parsing file - Datei wird verarbeitet + Datei wird analysiert - - Xz extraction failed. - Fehler beim Extrahieren der xz Datei. - - - - Sorry, this version of Oracle does not support xz compressed files. - Es tut uns Leid, diese Version von Oracle unterstützt keine xz komprimierten Dateien. - - - Failed to open Zip archive: %1. Fehler beim Öffnen des Zip Archivs: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. Fehler beim Extrahieren: Das Zip Archiv enthält mehr als eine Datei. - Zip extraction failed: %1. Fehler beim Extrahieren: %1. - Sorry, this version of Oracle does not support zipped files. Es tut uns Leid, diese Version von Oracle unterstützt keine Zip Archive. - - Failed to interpret downloaded data. - Interpretation der heruntergeladenen Daten fehlgeschlagen. + Do you want to try to download a fresh copy of the uncompressed file instead? + Möchtest du stattdessen eine neue Kopie der nicht komprimierten Datei herunterladen? - - Do you want to download the uncompressed file instead? - Möchten Sie stattdessen die unkomprimierte Datei herunterladen? - - - The file was retrieved successfully, but it does not contain any sets data. Die Datei wurde erfolgreich abgerufen, sie enthält aber keine Editionsdaten. - - - LoadSpoilersPage - - Save spoiler database - Speichere Spoilerdatenbank + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + Bitte geben Sie eine Quelle für die Liste von Editionen und Karten an. Sie können eine URL Adresse zum Herunterladen angeben oder eine Datei von Ihrem Computer verwenden. - - XML; spoiler database (*.xml) - XML; Spoilerdatenbank (*.xml) - - - - spoiler - Spoiler - - - - Spoilers import - Spoilerimport - - - - Please specify a compatible source for spoiler data. - Bitte geben Sie eine kompatible Quelle für Spoilerdaten an. - - - Download URL: Download URL: - - Local file: - Lokale Datei: - - - Restore default URL Standard-URL wiederherstellen - - Choose file... - Datei auswählen... - - - - The spoiler database will be saved at the following location: - Die Spoilerdatenbank wird in folgendem Pfad gespeichert: - - - - Save to a custom path (not recommended) - Speichere in benutzerdefiniertem Pfad (nicht empfohlen) + The provided URL is not valid. + Die eingegebene URL ist nicht gültig. LoadTokensPage - - Save token database - Speichere Spielsteindatenbank + Tokens source selection + Spielstein-Quellenauswahl - - XML; token database (*.xml) - XML; Spielsteindatenbank (*.xml) + Error + Fehler - - tokens - Spielsteine + Downloading (0MB) + Herunterladen (0MB) - - Tokens import - Spielsteinimport + Downloading (%1MB) + Herunterladen (%1MB) - - Please specify a compatible source for token data. - Bitte geben Sie eine kompatible Quelle für Spielsteindaten an. + Network error: %1. + Netzwerkfehler: %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + Bitte geben Sie eine Quelle für die Liste der Spielsteine an. Sie können eine URL Adresse zum Herunterladen angeben oder eine Datei von Ihrem Computer verwenden. - Download URL: Download URL: - - Local file: - Lokale Datei: - - - Restore default URL Standard-URL wiederherstellen - - Choose file... - Datei auswählen... - - - - The token database will be saved at the following location: - Die Spielsteindatenbank wird in folgendem Pfad gespeichert: - - - - Save to a custom path (not recommended) - Speichere in benutzerdefiniertem Pfad (nicht empfohlen) + The provided URL is not valid. + Die eingegebene URL ist nicht gültig. OracleImporter - Dummy set containing tokens Platzhalter Edition mit Spielsteinen @@ -284,250 +164,177 @@ OracleWizard - Oracle Importer Oracle Importer - - - OutroPage - - Finished - Fertig - - - - The wizard has finished. - Der Wizard ist fertig. - - - - You can now start using Cockatrice with the newly updated cards. - Sie können nun Cockatrice mit den aktuellen Karten verwenden. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Falls die Datenbanken nicht automatisch neu geladen werden, starten Sie bitte Cockatrice neu. + Save + Speichern SaveSetsPage - - - Error - Fehler - - - - No set has been imported. - Es wurden keine Editionen importiert. - - - Sets imported Editionen wurden importiert - - A cockatrice database file of %1 MB has been downloaded. - Eine Cockatrice-Datenbankdatei der Größe %1 MB wurde heruntergeladen. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + Die folgenden Editionen wurden importiert. Drücken Sie "Speichern" um die importierten Karten in der Cockatrice Datenbank abzuspeichern. - - The following sets have been found: - Die folgenden Sets wurden gefunden: + Save to the default path (recommended) + Im Standardverzeichnis abspeichern (Empfohlen) - - Press "Save" to store the imported cards in the Cockatrice database. - Drücken Sie "Speichern", um die importierten Karten in der Datenbank zu speichern. + Error + Fehler - - The card database will be saved at the following location: - Die Kartendatenbank wird in folgendem Pfad gespeichert: + No set has been imported. + Es wurden keine Editionen importiert. - - Save to a custom path (not recommended) - Speichere in benutzerdefiniertem Pfad (nicht empfohlen) - - - - &Save - &Speichern - - - Import finished: %1 cards. Importieren abgeschlossen: %1 Karten. - %1: %2 cards imported %1: %2 Karten importiert. - Save card database Kartendatenbank speichern - XML; card database (*.xml) XML; Kartendatenbank (*.xml) - + Success + Erfolgreich + + + The card database has been saved successfully to +%1 + Die Kartendatenbank wurde erfolgreich gespeichert: +%1 + + The file could not be saved to %1 Die Datei konnte nicht gespeichert werden: %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - Lade %1 Datei + Tokens imported + Spielsteine importiert - - %1 file (%1) - %1 Datei (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Spielsteine wurden importiert. Drücken Sie „Speichern“, um die importierten Spielsteine in der Cockatrice Spielsteindatenbank abzuspeichern. + + + Save to the default path (recommended) + Im Standardverzeichnis abspeichern (Empfohlen) + + + Save token database + Spielsteindatenbank speichern + + + XML; token database (*.xml) + XML; Tokendatenbank (*.xml) + + + Success + Erfolgreich + + + The token database has been saved successfully to +%1 + Die Spielsteindatenbank wurde erfolgreich im folgendem Pfad gespeichert: +%1 - - - - - Error Fehler - - The provided URL is not valid: - Die bereitgestellte URL ist nicht gültig: - - - - Downloading (0MB) - Herunterladen (0MB) - - - - Please choose a file. - Bitte wähle eine Datei aus. - - - - Cannot open file '%1'. - Datei '%1' kann nicht geöffnet werden. - - - - Downloading (%1MB) - Herunterladen (%1MB) - - - - Network error: %1. - Netzwerkfehler: %1. - - - The file could not be saved to %1 - Die Datei konnte nicht in %1 gespeichert werden + Die Datei konnte nicht gespeichert werden: +%1 UnZip - ZIP operation completed successfully. - ZIP-Operation erfolgreich abgeschlossen. + ZIP Operation erfolgreich abgeschlossen. - Failed to initialize or load zlib library. Fehler beim Initialisieren oder Laden der zlib Bibliothek. - zlib library error. - zlib Bibliotheksfehler. + zlib Bibliothek fehlerhaft - Unable to create or open file. Fehler beim Erstellen oder Öffnen der Datei. - Partially corrupted archive. Some files might be extracted. Teilweise fehlerhaftes Archiv. Manche Dateien wurden möglicherweise extrahiert. - Corrupted archive. Fehlerhaftes Archiv. - Wrong password. Falsches Passwort. - No archive has been created yet. Es wurde noch kein Archiv erstellt. - File or directory does not exist. Datei oder Verzeichnis existiert nicht. - File read error. - Dateilesefehler. + Lesefehler in der Datei. - File write error. - Dateischreibfehler. + Schreibfehler in der Datei. - File seek error. - Dateisuchfehler. + Suchfehler in der Datei. - Unable to create a directory. - Erstellen eines Verzeichnisses nicht möglich. + Fehler beim Erstellen des Verzeichnisses. - Invalid device. Ungültiges Gerät. - Invalid or incompatible zip archive. Ungültiges oder inkompatibles Zip Archiv. - Inconsistent headers. Archive might be corrupted. Unstimmiger Dateikopf. Das Archiv ist möglicherweise fehlerhaft. - Unknown error. Unbekannter Fehler. @@ -535,75 +342,44 @@ Zip - ZIP operation completed successfully. ZIP Operation erfolgreich abgeschlossen. - Failed to initialize or load zlib library. Fehler beim Initialisieren oder Laden der zlib Bibliothek. - zlib library error. - zlib Bibliotheksfehler. + zlib Bibliothek fehlerhaft - Unable to create or open file. Fehler beim Erstellen oder Öffnen der Datei. - No archive has been created yet. Es wurde noch kein Archiv erstellt. - File or directory does not exist. Datei oder Verzeichnis existiert nicht. - File read error. - Dateilesefehler. + Lesefehler in der Datei. - File write error. - Dateischreibfehler. + Schreibfehler in der Datei. - File seek error. - Dateisuchfehler. + Suchfehler in der Datei. - Unknown error. - Unbekannter Fehler. - - - - i18n - - - English - Deutsch (German) - - - - main - - - Only run in spoiler mode - Nur im Spoiler Modus ausführen - - - - Run in no-confirm background mode - Ausführen im Hintergrund-Modus ohne Bestätigung + Unbekannter Fehler \ No newline at end of file diff --git a/oracle/translations/oracle_el.ts b/oracle/translations/oracle_el.ts deleted file mode 100644 index 7934997cf..000000000 --- a/oracle/translations/oracle_el.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - Εισαγωγή - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Αυτός ο αυτόματος οδηγός θα εισάγει τη λίστα των σετ, καρτών και δειγμάτων (tokens) που θα χρησιμοποιηθούν από το Cockatrice. - - - - Interface language: - - - - - Version: - Έκδοση: - - - - LoadSetsPage - - - Source selection - Επιλογή πηγής - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - URL λήψης: - - - - Local file: - Τοπικό αρχείο: - - - - Restore default URL - Επαναφορά προκαθορισμένης διεύθυνσης URL - - - - Choose file... - Επιλέξτε αρχείο... - - - - Load sets file - Φόρτωση αρχείων - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - Σφάλμα - - - - The provided URL is not valid. - Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. - - - - Downloading (0MB) - Λήψη (0MB) - - - - Please choose a file. - Παρακαλώ διαλέξτε έναν αρχείο. - - - - Cannot open file '%1'. - Δεν είναι δυνατό να ανοίξει το αρχείο '% 1'. - - - - Downloading (%1MB) - Λήψη (% 1MB) - - - - Network error: %1. - Σφάλμα δικτύου: % 1. - - - - Parsing file - Ανάλυση αρχείου - - - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - - Failed to open Zip archive: %1. - Αποτυχία ανοίγματος αρχείου Zip:% 1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Η εξαγωγή zip απέτυχε: το αρχείο Zip δεν περιέχει ακριβώς ένα αρχείο. - - - - Zip extraction failed: %1. - Η εξαγωγή zip απέτυχε: % 1. - - - - Sorry, this version of Oracle does not support zipped files. - Λυπούμαστε, αυτή η έκδοση του Oracle δεν υποστηρίζει αρχεία τύπου zipped. - - - - Failed to interpret downloaded data. - - - - - Do you want to download the uncompressed file instead? - - - - - The file was retrieved successfully, but it does not contain any sets data. - Το αρχείο ανακτήθηκε με επιτυχία, αλλά δεν περιέχει σύνολα δεδομένων. - - - - LoadSpoilersPage - - - Save spoiler database - - - - - XML; spoiler database (*.xml) - - - - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - LoadTokensPage - - - Save token database - - - - - XML; token database (*.xml) - - - - - tokens - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - - Download URL: - URL λήψης: - - - - Local file: - - - - - Restore default URL - Επαναφορά προκαθορισμένης διεύθυνσης URL - - - - Choose file... - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - OracleImporter - - - Dummy set containing tokens - Εικονικό σετ που περιέχει tokens - - - - OracleWizard - - - Oracle Importer - Εισαγωγέας Oracle - - - - OutroPage - - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - - - - - SaveSetsPage - - - - Error - Σφάλμα - - - - No set has been imported. - Δεν έχει εισαχθεί κανένα σετ. - - - - Sets imported - Εισαγόμενα σετς - - - - A cockatrice database file of %1 MB has been downloaded. - - - - - The following sets have been found: - - - - - Press "Save" to store the imported cards in the Cockatrice database. - - - - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - Η εισαγωγή ολοκληρώθηκε: % 1 κάρτες. - - - - %1: %2 cards imported - %1: %2 κάρτες εισήχθησαν - - - - Save card database - Αποθήκευση κάρτας βάσης δεδομένων - - - - XML; card database (*.xml) - XML; κάρτα βάσης δεδομένων (* .xml) - - - - The file could not be saved to %1 - Δεν ήταν δυνατή η αποθήκευση του αρχείου στο %1 - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - - - - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - The file could not be saved to %1 - - - - - UnZip - - - ZIP operation completed successfully. - Η λειτουργία ZIP ολοκληρώθηκε με επιτυχία. - - - - Failed to initialize or load zlib library. - Δεν ήταν δυνατή η προετοιμασία ή η φόρτωση της βιβλιοθήκης zlib. - - - - zlib library error. - Σφάλμα βιβλιοθήκης zlib. - - - - Unable to create or open file. - Δεν είναι δυνατή η δημιουργία ή το άνοιγμα αρχείου. - - - - Partially corrupted archive. Some files might be extracted. - Μερικώς κατεστραμμένο αρχείο. Κάποια αρχεία ενδέχεται να εξαχθούν. - - - - Corrupted archive. - Κατεστραμμένο αρχείο. - - - - Wrong password. - Λάθος κωδικός. - - - - No archive has been created yet. - Δεν έχει δημιουργηθεί κανένα αρχείο ακόμα. - - - - File or directory does not exist. - Το αρχείο ή ο κατάλογος δεν υπάρχει. - - - - File read error. - Σφάλμα ανάγνωσης αρχείου. - - - - File write error. - Σφάλμα εγγραφής αρχείου. - - - - File seek error. - Σφάλμα αναζήτησης αρχείου. - - - - Unable to create a directory. - Δεν είναι δυνατή η δημιουργία ενός καταλόγου. - - - - Invalid device. - Μη έγκυρη συσκευή. - - - - Invalid or incompatible zip archive. - Μη έγκυρο ή ασυμβίβαστο zip αρχείο. - - - - Inconsistent headers. Archive might be corrupted. - Αντιφατικές κεφαλίδες. Το αρχείο μπορεί να είναι κατεστραμμένο. - - - - Unknown error. - Άγνωστο σφάλμα. - - - - Zip - - - ZIP operation completed successfully. - Η λειτουργία ZIP ολοκληρώθηκε με επιτυχία. - - - - Failed to initialize or load zlib library. - Δεν ήταν δυνατή η προετοιμασία ή η φόρτωση της βιβλιοθήκης zlib. - - - - zlib library error. - Σφάλμα βιβλιοθήκης zlib. - - - - Unable to create or open file. - Δεν είναι δυνατή η δημιουργία ή το άνοιγμα αρχείου. - - - - No archive has been created yet. - Δεν έχει δημιουργηθεί αρχείο ακόμα. - - - - File or directory does not exist. - Το αρχείο ή ο κατάλογος δεν υπάρχει. - - - - File read error. - Σφάλμα ανάγνωσης αρχείου. - - - - File write error. - Σφάλμα εγγραφής αρχείου. - - - - File seek error. - Σφάλμα αναζήτησης αρχείου. - - - - Unknown error. - Άγνωστο σφάλμα. - - - - i18n - - - English - Ελληνικά (Greek) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_en.ts b/oracle/translations/oracle_en.ts new file mode 100644 index 000000000..edb2cf377 --- /dev/null +++ b/oracle/translations/oracle_en.ts @@ -0,0 +1,382 @@ + + + + + IntroPage + + Introduction + + + + English + English + + + Language: + + + + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + + + + + LoadSetsPage + + Source selection + + + + Local file: + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + LoadTokensPage + + Tokens source selection + + + + Error + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + + + + diff --git a/oracle/translations/oracle_en@pirate.ts b/oracle/translations/oracle_en@pirate.ts index 13a57750b..20c40fb7a 100644 --- a/oracle/translations/oracle_en@pirate.ts +++ b/oracle/translations/oracle_en@pirate.ts @@ -1,252 +1,161 @@ - + IntroPage - Introduction - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. + English + English, arr! (Pirate English) + + + Language: - - Interface language: - - - - - Version: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. LoadSetsPage - Source selection - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - - - - Local file: - - Restore default URL - - - - Choose file... - Load sets file - - Sets file (%1) - Sets JSON file (%1) + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) - - - - - - - Error Cap'n? Thar be a problem - - The provided URL is not valid. - - - - Downloading (0MB) - Please choose a file. - Cannot open file '%1'. - Downloading (%1MB) - Network error: %1. Cap'n? Thar be a problem wi' t' smoke signals: %1. - Parsing file - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip extraction failed: %1. - Sorry, this version of Oracle does not support zipped files. - - Failed to interpret downloaded data. + Do you want to try to download a fresh copy of the uncompressed file instead? - - Do you want to download the uncompressed file instead? - - - - The file was retrieved successfully, but it does not contain any sets data. - - - LoadSpoilersPage - - Save spoiler database + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - XML; spoiler database (*.xml) - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - Restore default URL - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error + Cap'n? Thar be a problem + + + Downloading (0MB) - - Tokens import + Downloading (%1MB) - - Please specify a compatible source for token data. + Network error: %1. + Cap'n? Thar be a problem wi' t' smoke signals: %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - Download URL: - Restore default URL - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens @@ -254,139 +163,102 @@ OracleWizard - Oracle Importer - - - OutroPage - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. + Save SaveSetsPage - - - Error - Cap'n? Thar be a problem - - - - No set has been imported. - - - - Sets imported - - A cockatrice database file of %1 MB has been downloaded. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - The following sets have been found: + Save to the default path (recommended) - - Press "Save" to store the imported cards in the Cockatrice database. + Error + Cap'n? Thar be a problem + + + No set has been imported. - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - Import finished: %1 cards. - %1: %2 cards imported - Save card database - XML; card database (*.xml) - + Success + + + + The card database has been saved successfully to +%1 + + + The file could not be saved to %1 - SimpleDownloadFilePage + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + - - - Error - + Cap'n? Thar be a problem - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 @@ -394,87 +266,70 @@ UnZip - ZIP operation completed successfully. - Failed to initialize or load zlib library. - zlib library error. Cap'n? Thar be a problem wi' t' zlib library. - Unable to create or open file. - Partially corrupted archive. Some files might be extracted. - Corrupted archive. - Wrong password. - No archive has been created yet. - File or directory does not exist. - File read error. - File write error. - File seek error. - Unable to create a directory. - Invalid device. - Invalid or incompatible zip archive. - Inconsistent headers. Archive might be corrupted. - Unknown error. Cap'n? Thar be a unknown problem wi' t' ship. @@ -482,75 +337,44 @@ Zip - ZIP operation completed successfully. - Failed to initialize or load zlib library. - zlib library error. Cap'n? Thar be a problem wi' t' zlib library. - Unable to create or open file. - No archive has been created yet. - File or directory does not exist. - File read error. - File write error. - File seek error. - Unknown error. Cap'n? Thar be a unknown problem wi' t' ship. - - i18n - - - English - English, arr! (Pirate English) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_en_US.ts b/oracle/translations/oracle_en_US.ts deleted file mode 100644 index 4b664bf57..000000000 --- a/oracle/translations/oracle_en_US.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - Introduction - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - - - - Interface language: - Interface language: - - - - Version: - Version: - - - - LoadSetsPage - - - Source selection - Source selection - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - Download URL: - Download URL: - - - - Local file: - Local file: - - - - Restore default URL - Restore default URL - - - - Choose file... - Choose file... - - - - Load sets file - Load sets file - - - - Sets file (%1) - Sets JSON file (%1) - Sets file (%1) - - - - - - - - - - Error - Error - - - - The provided URL is not valid. - The provided URL is not valid. - - - - Downloading (0MB) - Downloading (0MB) - - - - Please choose a file. - Please choose a file. - - - - Cannot open file '%1'. - Cannot open file '%1'. - - - - Downloading (%1MB) - Downloading (%1MB) - - - - Network error: %1. - Network error: %1. - - - - Parsing file - Parsing file - - - - Xz extraction failed. - Xz extraction failed. - - - - Sorry, this version of Oracle does not support xz compressed files. - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. - Failed to open Zip archive: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip extraction failed: the Zip archive doesn't contain exactly one file. - - - - Zip extraction failed: %1. - Zip extraction failed: %1. - - - - Sorry, this version of Oracle does not support zipped files. - Sorry, this version of Oracle does not support zipped files. - - - - Failed to interpret downloaded data. - Failed to interpret downloaded data. - - - - Do you want to download the uncompressed file instead? - Do you want to download the uncompressed file instead? - - - - The file was retrieved successfully, but it does not contain any sets data. - The file was retrieved successfully, but it does not contain any sets data. - - - - LoadSpoilersPage - - - Save spoiler database - Save spoiler database - - - - XML; spoiler database (*.xml) - XML; spoiler database (*.xml) - - - - spoiler - spoiler - - - - Spoilers import - Spoilers import - - - - Please specify a compatible source for spoiler data. - Please specify a compatible source for spoiler data. - - - - Download URL: - Download URL: - - - - Local file: - Local file: - - - - Restore default URL - Restore default URL - - - - Choose file... - Choose file... - - - - The spoiler database will be saved at the following location: - The spoiler database will be saved at the following location: - - - - Save to a custom path (not recommended) - Save to a custom path (not recommended) - - - - LoadTokensPage - - - Save token database - Save token database - - - - XML; token database (*.xml) - XML; token database (*.xml) - - - - tokens - tokens - - - - Tokens import - Tokens import - - - - Please specify a compatible source for token data. - Please specify a compatible source for token data. - - - - Download URL: - Download URL: - - - - Local file: - Local file: - - - - Restore default URL - Restore default URL - - - - Choose file... - Choose file... - - - - The token database will be saved at the following location: - The token database will be saved at the following location: - - - - Save to a custom path (not recommended) - Save to a custom path (not recommended) - - - - OracleImporter - - - Dummy set containing tokens - Dummy set containing tokens - - - - OracleWizard - - - Oracle Importer - Oracle Importer - - - - OutroPage - - - Finished - Finished - - - - The wizard has finished. - The wizard has finished. - - - - You can now start using Cockatrice with the newly updated cards. - You can now start using Cockatrice with the newly updated cards. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - If the card databases don't reload automatically, restart the Cockatrice client. - - - - SaveSetsPage - - - - Error - Error - - - - No set has been imported. - No set has been imported. - - - - Sets imported - Sets imported - - - - A cockatrice database file of %1 MB has been downloaded. - A cockatrice database file of %1 MB has been downloaded. - - - - The following sets have been found: - The following sets have been found: - - - - Press "Save" to store the imported cards in the Cockatrice database. - Press "Save" to store the imported cards in the Cockatrice database. - - - - The card database will be saved at the following location: - The card database will be saved at the following location: - - - - Save to a custom path (not recommended) - Save to a custom path (not recommended) - - - - &Save - &Save - - - - Import finished: %1 cards. - Import finished: %1 cards. - - - - %1: %2 cards imported - %1: %2 cards imported - - - - Save card database - Save card database - - - - XML; card database (*.xml) - XML; card database (*.xml) - - - - The file could not be saved to %1 - The file could not be saved to %1 - - - - SimpleDownloadFilePage - - - Load %1 file - Load %1 file - - - - %1 file (%1) - %1 file (%1) - - - - - - - - Error - Error - - - - The provided URL is not valid: - The provided URL is not valid: - - - - Downloading (0MB) - Downloading (0MB) - - - - Please choose a file. - Please choose a file. - - - - Cannot open file '%1'. - Cannot open file '%1'. - - - - Downloading (%1MB) - Downloading (%1MB) - - - - Network error: %1. - Network error: %1. - - - - The file could not be saved to %1 - The file could not be saved to %1 - - - - UnZip - - - ZIP operation completed successfully. - ZIP operation completed successfully. - - - - Failed to initialize or load zlib library. - Failed to initialize or load zlib library. - - - - zlib library error. - zlib library error. - - - - Unable to create or open file. - Unable to create or open file. - - - - Partially corrupted archive. Some files might be extracted. - Partially corrupted archive. Some files might be extracted. - - - - Corrupted archive. - Corrupted archive. - - - - Wrong password. - Wrong password. - - - - No archive has been created yet. - No archive has been created yet. - - - - File or directory does not exist. - File or directory does not exist. - - - - File read error. - File read error. - - - - File write error. - File write error. - - - - File seek error. - File seek error. - - - - Unable to create a directory. - Unable to create a directory. - - - - Invalid device. - Invalid device. - - - - Invalid or incompatible zip archive. - Invalid or incompatible zip archive. - - - - Inconsistent headers. Archive might be corrupted. - Inconsistent headers. Archive might be corrupted. - - - - Unknown error. - Unknown error. - - - - Zip - - - ZIP operation completed successfully. - ZIP operation completed successfully. - - - - Failed to initialize or load zlib library. - Failed to initialize or load zlib library. - - - - zlib library error. - zlib library error. - - - - Unable to create or open file. - Unable to create or open file. - - - - No archive has been created yet. - No archive has been created yet. - - - - File or directory does not exist. - File or directory does not exist. - - - - File read error. - File read error. - - - - File write error. - File write error. - - - - File seek error. - File seek error. - - - - Unknown error. - Unknown error. - - - - i18n - - - English - English - - - - main - - - Only run in spoiler mode - Only run in spoiler mode - - - - Run in no-confirm background mode - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_es.ts b/oracle/translations/oracle_es.ts index eeb9f71bd..557147fdc 100644 --- a/oracle/translations/oracle_es.ts +++ b/oracle/translations/oracle_es.ts @@ -1,282 +1,162 @@ - + IntroPage - Introduction Introducción - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Este instalador importará la lista de ediciones, cartas y fichas que va a usar Cockatrice. + English + Español (Spanish) - - Interface language: - Idioma de la interfaz: + Language: + Idioma - - Version: - Versión: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + Este asistente importará la lista de los sets y cartas que serán usadas por Cockatrice. +Necesitarás especificar la URL o el nombre de archivo que será usado como origen. LoadSetsPage - Source selection Seleccionar origen - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Por favor especifica un origen compatible para la lista de sets y cartas. Puedes especificar la URL de donde descargarla o usar un archivo existente de tu ordenador. - - - - Download URL: - URL de descarga: - - - Local file: Archivo local: - - Restore default URL - Restablecer URL predeterminada - - - Choose file... Elegir archivo... - Load sets file Cargar archivo de ediciones - - Sets file (%1) - Sets JSON file (%1) - + Sets JSON file (*.json *.zip) + Archivo de sets en formato JSON (*.json *.zip) + + + Sets JSON file (*.json) + Archivo de sets en formato JSON (*.json) - - - - - - - Error Error - - The provided URL is not valid. - La URL suministrada no es válida. - - - Downloading (0MB) Descargando (0MB) - Please choose a file. Por favor elija un archivo. - Cannot open file '%1'. No se puede abrir el archivo '%1' - Downloading (%1MB) Descargando (%1MB) - Network error: %1. Error de red: %1 - Parsing file Procesando archivo - - Xz extraction failed. - Extracción de Xz fallida - - - - Sorry, this version of Oracle does not support xz compressed files. - Lo sentimos, esta versión de Oracle no soporta archivos comprimidos xz - - - Failed to open Zip archive: %1. Error al abrir el archivo Zip: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. Fallo al extraer el contenido: el Zip contiene más de un archivo. - Zip extraction failed: %1. Error al extraer el contenido del Zip: %1. - Sorry, this version of Oracle does not support zipped files. Lo sentimos, esta versión de Oracle no soporta archivos comprimidos. - - Failed to interpret downloaded data. - No se pudieron interpretar los datos descargados. + Do you want to try to download a fresh copy of the uncompressed file instead? + ¿Prefieres intentar descargar una copia nueva del fichero descomprimido? - - Do you want to download the uncompressed file instead? - ¿Prefieres descargar el archivo sin comprimir? - - - The file was retrieved successfully, but it does not contain any sets data. El archivo fue cargado correctamente pero no contiene datos sobre ningún set. - - - LoadSpoilersPage - - Save spoiler database - Guardar la base de datos de spoilers + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + Por favor especifica un origen para la lista de sets y cartas. Puedes especificar la URL de donde descargarla o usar un archivo existente de tu ordenador. - - XML; spoiler database (*.xml) - XML; base de datos de spoilers (*.xml) - - - - spoiler - - - - - Spoilers import - Importar spoilers - - - - Please specify a compatible source for spoiler data. - Por favor elija un origen compatible para los datos de los spoilers. - - - Download URL: URL de descarga: - - Local file: - - - - Restore default URL - Restaurar URL por defecto + Restablecer URL predeterminada - - Choose file... - - - - - The spoiler database will be saved at the following location: - La base de datos de los spoilers será guardada en la siguiente ubicación: - - - - Save to a custom path (not recommended) - Guardar en una ruta personalizada (no recomendado) + The provided URL is not valid. + La URL suministrada no es válida. LoadTokensPage - - Save token database - Guardar base de datos de fichas + Tokens source selection + Selección de origen de tokens - - XML; token database (*.xml) - XML; base de datos de fichas (*.xml) + Error + Error - - tokens - + Downloading (0MB) + Descargando (0MB) - - Tokens import - Importar tokens + Downloading (%1MB) + Descargando (%1MB) - - Please specify a compatible source for token data. - Por favor elija un origen compatible para los datos de las fichas. + Network error: %1. + Error de red: %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + Por favor especifica un origen para la lista de tokens. Puedes especificar la URL de donde descargarla o usar un archivo existente en tu ordenador. - Download URL: URL de descarga: - - Local file: - - - - Restore default URL Restablecer URL predeterminada - - Choose file... - - - - - The token database will be saved at the following location: - La base de datos de las fichas será guardada en la siguiente ubicación: - - - - Save to a custom path (not recommended) - Guardar en una ruta personalizada (no recomendado) + The provided URL is not valid. + La URL suministrada no es válida. OracleImporter - Dummy set containing tokens Set dedicado para tokens @@ -284,161 +164,104 @@ OracleWizard - Oracle Importer Importador de Oracle - - - OutroPage - - Finished - Completado - - - - The wizard has finished. - El asistente ha terminado. - - - - You can now start using Cockatrice with the newly updated cards. - Ahora puedes usar Cockatrice con las cartas actualizadas. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Si las bases de datos de cartas no se recargan automáticamente, reinicia el cliente de Cockatrice. + Save + Guardar SaveSetsPage - - - Error - Error - - - - No set has been imported. - Ningún set ha sido importado. - - - Sets imported Sets importados - - A cockatrice database file of %1 MB has been downloaded. - Se ha descargado un archivo de base de datos cockatrice de %1 MB. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + Los siguientes sets han sido importados. Pulsa "Guardar" para guardar las cartas importadas en la base de datos de Cockatrice. - - The following sets have been found: - Las siguientes ediciones han sido encontradas: + Save to the default path (recommended) + Guardar en la ruta por defecto (recomendado) - - Press "Save" to store the imported cards in the Cockatrice database. - Pulsa "Guardar" para guardar las cartas importadas en la base de datos de Cockatrice. + Error + Error - - The card database will be saved at the following location: - La base de datos de cartas ha sido guardada en la siguiente ubicación: + No set has been imported. + Ningún set ha sido importado. - - Save to a custom path (not recommended) - Guardar en una ruta personalizada (no recomendado) - - - - &Save - &Guardar - - - Import finished: %1 cards. Importación terminada: %1 cartas. - %1: %2 cards imported %1: %2 cartas importadas - Save card database Guardar base de datos de cartas - XML; card database (*.xml) XML; base de datos de cartas (*.xml) - + Success + Éxito + + + The card database has been saved successfully to +%1 + La base de datos de cartas ha sido guardada correctamente en +%1 + + The file could not be saved to %1 El archivo no ha podido ser guardado en %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - + Tokens imported + Tokens importados - - %1 file (%1) - + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Los tokens han sido importados. Pulsa "Guardar" para guardar los tokens importados en la base de datos de tokens de Cockatrice. + + + Save to the default path (recommended) + Guardar en la ruta por defecto (recomendado) + + + Save token database + Base de datos de tokens: + + + XML; token database (*.xml) + XML; base de datos de tokens (*.xml) + + + Success + Éxito + + + The token database has been saved successfully to +%1 + La base de datos de cartas ha sido guardada correctamente en +%1 - - - - - Error Error - - The provided URL is not valid: - La URL proporcionada no es válida: - - - - Downloading (0MB) - Descargando (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Descargando (%1MB) - - - - Network error: %1. - Error de red: %1. - - - The file could not be saved to %1 El archivo no ha podido ser guardado en %1 @@ -446,163 +269,115 @@ UnZip - ZIP operation completed successfully. - La operación ZIP se completó con éxito + La operación se ha completado correctamente. - - Failed to initialize or load zlib library. - No se pudo inicializar o cargar la biblioteca zlib - - - - zlib library error. - Error de biblioteca zlib. - - - - Unable to create or open file. - No se puede crear o abrir el archivo. - - - - Partially corrupted archive. Some files might be extracted. - Archivo parcialmente dañado. Es posible que se extraigan algunos archivos. - - - - Corrupted archive. - Archivo corrupto. - - - - Wrong password. - Contraseña incorrecta. - - - - No archive has been created yet. - Aún no se ha creado ningún archivo. - - - - File or directory does not exist. - El archivo o directorio no existe. - - - - File read error. - error de lectura de archivo - - - - File write error. - Error de escritura del archivo. - - - - File seek error. - Error de búsqueda de archivo. - - - - Unable to create a directory. - No se puede crear un directorio - - - - Invalid device. - Dispositivo no válido. - - - - Invalid or incompatible zip archive. - Archivo zip no válido o incompatible - - - - Inconsistent headers. Archive might be corrupted. - Encabezados inconsistentes. Es posible que el archivo esté dañado. - - - - Unknown error. - Encabezados inconsistentes. Es posible que el archivo esté dañado. - - - - Zip - - - ZIP operation completed successfully. - La operación ZIP se completó con éxito. - - - Failed to initialize or load zlib library. Fallo al iniciar o cargar la librería zlib. - zlib library error. Error en la librería zlib. - Unable to create or open file. - No es posible crear o abrir el archivo. + No se ha podido crear o abrir el archivo. + + + Partially corrupted archive. Some files might be extracted. + Archivo parcialmente dañado. Algunos datos han podido ser extraídos. + + + Corrupted archive. + Archivo dañado. + + + Wrong password. + Contraseña incorrecta. - No archive has been created yet. No se ha creado ningún archivo todavía. - File or directory does not exist. - El archivo o directorio no existe. + El archivo o directorio no existen. - File read error. Error al leer el archivo. - File write error. Error al escribir en archivo. - File seek error. Error al buscar en el archivo. - + Unable to create a directory. + No es posible crear el directorio. + + + Invalid device. + Dispositivo invalido. + + + Invalid or incompatible zip archive. + Archivo zip inválido o incompatible. + + + Inconsistent headers. Archive might be corrupted. + Cabezeras inconsistentes. El archivo podría estar dañado. + + Unknown error. Error desconocido. - i18n + Zip - - English - Español (Spanish) - - - - main - - - Only run in spoiler mode - Sólo ejecutar en modo spoiler + ZIP operation completed successfully. + Operación de descompresión completada correctamente. - - Run in no-confirm background mode - + Failed to initialize or load zlib library. + Fallo al iniciar o cargar la librería zlib. + + + zlib library error. + Error de biblioteca zlib. + + + Unable to create or open file. + No es posible crear o abrir el archivo. + + + No archive has been created yet. + No se ha creado ningún archivo todavía. + + + File or directory does not exist. + El archivo o directorio no existe. + + + File read error. + Error al leer el archivo. + + + File write error. + Error al escribir en archivo. + + + File seek error. + Error al buscar en el archivo. + + + Unknown error. + Error desconocido. \ No newline at end of file diff --git a/oracle/translations/oracle_et.ts b/oracle/translations/oracle_et.ts index 9f2c560cd..6e0b8c997 100644 --- a/oracle/translations/oracle_et.ts +++ b/oracle/translations/oracle_et.ts @@ -1,532 +1,336 @@ - + IntroPage - Introduction Sissejuhatus - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - See võlur impordib Cockatrice’is kasutatava nimekirja komplektidest, kaartidest ja märgistustest. + English + Eesti Keel (Estonian) - - Interface language: + Language: + Keel: + + + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. - - - Version: - Versioon: - LoadSetsPage - Source selection Allika valik - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Palun täpsusta ühilduv allikas komplektide ja kaartide nimekirja jaoks. Võid täpsustada URL-i aadressi, mis laaditakse alla või kasutada olemasolevat faili oma arvutist. - - - - Download URL: - Allalaadimise URL: - - - Local file: Fail arvutis: - - Restore default URL - Taasta tavaURL - - - Choose file... Valige fail.... - Load sets file - Lae komplektide kaust - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + Error Viga - - The provided URL is not valid. - Antud URl pole kehtiv. - - - Downloading (0MB) Allalaadimine (0MB) - Please choose a file. Palun valige fail. - Cannot open file '%1'. Ei suudeta avada '%1'. - Downloading (%1MB) Allalaadimine (%1MB) - Network error: %1. Võrgu viga: %1. - Parsing file Faili hankimine - - Xz extraction failed. - Xzi lahtipakkimine nurjus. - - - - Sorry, this version of Oracle does not support xz compressed files. - Vabandame, aga antud Oracle’i versioon ei toeta xz kokkupakitud faile. - - - Failed to open Zip archive: %1. Zip-arhiivi avamine ebaõnnestus: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. Zip-i lahtipakkimine ebaõnnestus: Zip-arhiiv sisaldab rohkem faile kui üks. - Zip extraction failed: %1. Zipi lahtipakkimine ebaõnnestus: %1. - Sorry, this version of Oracle does not support zipped files. Vabandame, aga antud Oracle versioon ei toeta kokkupakitud faile. - - Failed to interpret downloaded data. - + Do you want to try to download a fresh copy of the uncompressed file instead? + Soovite hoopis alla laadida pakkimata faili värske koopia? - - Do you want to download the uncompressed file instead? - Soovid alla laadida hoopis pakkimata faili? - - - The file was retrieved successfully, but it does not contain any sets data. Fail on edukalt alla laetud, ent ei sisalda andmeid. - - - LoadSpoilersPage - - Save spoiler database - Salvesta spoileri andmebaas - - - - XML; spoiler database (*.xml) - XML; spoileri andmebaas (*.xml) - - - - spoiler + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - Spoilers import - Spoilerite import - - - - Please specify a compatible source for spoiler data. - Täpsusta spoileri andmetega ühilduv allikas. - - - Download URL: - Allalaadimise URL: - - - - Local file: - Restore default URL - Taasta tavaURL - - - - Choose file... - - The spoiler database will be saved at the following location: - Spoileri andmebaas salvestatakse järgmisesse asukohta: - - - - Save to a custom path (not recommended) - Salvesta enda määratud asukohta (pole soovitatav) + The provided URL is not valid. + LoadTokensPage - - Save token database - Salvesta märgistuste andmebaas - - - - XML; token database (*.xml) - XML; märgistuste andmebaas (*.xml) - - - - tokens + Tokens source selection - - Tokens import - Märgistuste importimine + Error + - - Please specify a compatible source for token data. - Täpsusta märgistuste andmetega ühilduv allikas. + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + - Download URL: - Allalaadimise URL: - - - - Local file: - Restore default URL - Taasta tavaURL - - - - Choose file... - - The token database will be saved at the following location: - Märgistuste andmebaas salvestatakse järgmisesse asukohta: - - - - Save to a custom path (not recommended) - Salvesta enda määratud asukohta (pole soovitatav) + The provided URL is not valid. + OracleImporter - Dummy set containing tokens - Nukk-komplekt mis sisaldab märgistusi + OracleWizard - Oracle Importer - Oracle sissetooja - - - - OutroPage - - - Finished - Valmis + Oracle importija - - The wizard has finished. - Võlur on lõpetanud. - - - - You can now start using Cockatrice with the newly updated cards. - Nüüd saad Cockatrice’i kasutada uhiuute kaartidega. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Kui kaardi andmebaasid ei lae ise, taaskäivita Cockatrice’i klient. + Save + Salvesta SaveSetsPage - - + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + Salvesta tavalisse asukohta (soovitatud) + + Error Viga - No set has been imported. - Komplekti pole sisse toodud. - - - - Sets imported - Allalaetud komplektid - - - - A cockatrice database file of %1 MB has been downloaded. - - The following sets have been found: - Leiti järgmised komplektid: - - - - Press "Save" to store the imported cards in the Cockatrice database. - Vajuta „Salvesta“, et salvestada imporditud kaardid Cockatrice’i andmebaasi. - - - - The card database will be saved at the following location: - Kaardi andmebaas salvestatakse järgmisesse asukohta: - - - - Save to a custom path (not recommended) - Salvesta enda määratud asukohta (pole soovitatav) - - - - &Save - &Salvesta - - - Import finished: %1 cards. %1 kaarti imporditi edukalt. - %1: %2 cards imported %1: imporditi %2 kaarti - Save card database - Salvesta kaartide andmebaas + Salvesta kaardi andmebaas - XML; card database (*.xml) - XML; kaartide andmebaas (*.xml) + XML; kaardi andmebaas (*.xml) + + + Success + Valmis + + + The card database has been saved successfully to +%1 + Kaardi andmebaas salvestati edukalt asukohta +%1 - The file could not be saved to %1 Faili salvestamine asukohta %1 ebaõnnestus - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error - Viga - - - - The provided URL is not valid: - - Downloading (0MB) - Allalaadimine (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Allalaadimine (%1MB) - - - - Network error: %1. - Võrgu viga: %1. - - - The file could not be saved to %1 - Faili salvestamine asukohta %1 nurjus + UnZip - ZIP operation completed successfully. Zip-i tegevus oli edukas. - Failed to initialize or load zlib library. Zlibi kogu ei suudetud ette valmistada või laadida. - zlib library error. zlibi kogu viga. - Unable to create or open file. Faili ei suudetud luua või avada. - Partially corrupted archive. Some files might be extracted. Osaliselt vigane arhiiv. Ainult osa faile võidakse lahti pakkida. - Corrupted archive. Kahjustunud arhiiv. - Wrong password. Vale parool. - No archive has been created yet. Loodud arhiivid puuduvad. - File or directory does not exist. Faili või asukohta pole olemas. - File read error. Faili lugemise viga. - File write error. Faili kirjutamise viga. - File seek error. - Faili otsimise viga + - Unable to create a directory. Asukoha loomine ebaõnnestus. - Invalid device. Vigane seade. - Invalid or incompatible zip archive. Vigane või mittetoetatav zip-arhiiv. - Inconsistent headers. Archive might be corrupted. - Ebajäriekindlad päised. Arhiiv võib olla rikutud. + - Unknown error. Tundmatu viga. @@ -534,75 +338,44 @@ Zip - ZIP operation completed successfully. Zip-tegevus on valmis. - Failed to initialize or load zlib library. Zlib kogu ei suudetud ette valmistada või laadida. - zlib library error. zlibi kogu viga. - Unable to create or open file. Faili loomine või avamine ebaõnnestus. - No archive has been created yet. Loodud arhiivid puuduvad. - File or directory does not exist. Faili või asukohta pole olemas. - File read error. Faili lugemise viga. - File write error. Faili kirjutamise viga. - File seek error. - Faili otsimise viga. + - Unknown error. Tundmatu viga. - - i18n - - - English - Eesti Keel (Estonian) - - - - main - - - Only run in spoiler mode - Käivita vaid spoileri režiimis - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_fi.ts b/oracle/translations/oracle_fi.ts deleted file mode 100644 index c7657cf7a..000000000 --- a/oracle/translations/oracle_fi.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - Johdanto - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Tämä työkalu asentaa Cockatricessa käytettävät setit, kortit ja tokenit. - - - - Interface language: - Käyttöliittymän kieli: - - - - Version: - Versio: - - - - LoadSetsPage - - - Source selection - Lähteen määritys - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Ole hyvä ja määritä lähde setti- ja korttilistalle. Voit määrittää ladattavan URL-osoitteen tai käyttää tietokoneellasi olevaa tiedostoa. - - - - Download URL: - Ladattavan tiedoston URL-osoite: - - - - Local file: - Paikallinen tiedosto: - - - - Restore default URL - Palauta oletus-URL - - - - Choose file... - Valitse tiedosto... - - - - Load sets file - Ladatta ekspansiotiedosto - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - Virhe - - - - The provided URL is not valid. - Määritetty URL on virheellinen. - - - - Downloading (0MB) - Ladataan (0MB) - - - - Please choose a file. - Valitse tiedosto. - - - - Cannot open file '%1'. - Ei voida avata tiedostoa '%1'. - - - - Downloading (%1MB) - Ladataan (%1MB) - - - - Network error: %1. - Yhteysvirhe: %1. - - - - Parsing file - Jäsennellään tiedostoa - - - - Xz extraction failed. - Xz purku epäonnistui. - - - - Sorry, this version of Oracle does not support xz compressed files. - Pahoittelut. Tämä versio Oraclesta ei tue xz-pakattuja tiedostoja. - - - - Failed to open Zip archive: %1. - Zip-tiedoston avaaminen epäonnistui: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip-tiedoston purkaminen epäonnistui: Zip-tiedosto ei sisällä tarkalleen yhtä tiedostoa. - - - - Zip extraction failed: %1. - Zip-tiedoston purkaminen epäonnistuie: %1. - - - - Sorry, this version of Oracle does not support zipped files. - Pahoittelut. Tämä versio Oraclesta ei tue Zip-pakattuja tiedostoja. - - - - Failed to interpret downloaded data. - Ladattujen tietojen tulkitseminen epäonnistui. - - - - Do you want to download the uncompressed file instead? - Haluatko sensijaan ladata pakkaamattoman tiedoston? - - - - The file was retrieved successfully, but it does not contain any sets data. - Tiedosto palautettiin onnistuneesti, mutta se ei sisällä yhdenkään ekspansion dataa. - - - - LoadSpoilersPage - - - Save spoiler database - Tallenna spoileritietokanta - - - - XML; spoiler database (*.xml) - XML; spoileritietokanta (*.xml) - - - - spoiler - - - - - Spoilers import - Spoilerien lataus - - - - Please specify a compatible source for spoiler data. - Ole hyvä ja osoita yhteensopiva spoileridatan lähde. - - - - Download URL: - Ladattavan tiedoston URL-osoite: - - - - Local file: - - - - - Restore default URL - Palauta oletus-URL - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - Spoileritietokanta tallennetaan seuraavaan sijaintiin: - - - - Save to a custom path (not recommended) - Tallenna mukautettuun sijaintiin (ei-suositeltu) - - - - LoadTokensPage - - - Save token database - Tallenna tokenitietokanta - - - - XML; token database (*.xml) - XML; tokenitietokanta (*.xml) - - - - tokens - - - - - Tokens import - Tokenien lataus - - - - Please specify a compatible source for token data. - Ole hyvä ja osoita yhteensopiva tokenidatan lähde. - - - - Download URL: - Ladattavan tiedoston URL-osoite: - - - - Local file: - - - - - Restore default URL - Palauta oletus-URL - - - - Choose file... - - - - - The token database will be saved at the following location: - Tokenitietokanta tallennetaan seuraavaan sijaintiin: - - - - Save to a custom path (not recommended) - Tallenna mukautettuun sijaintiin (ei-suositeltu) - - - - OracleImporter - - - Dummy set containing tokens - Tokeneja sisältävä mallisetti - - - - OracleWizard - - - Oracle Importer - Oracle-lataaja - - - - OutroPage - - - Finished - Valmis - - - - The wizard has finished. - Asennusohjelma on valmis. - - - - You can now start using Cockatrice with the newly updated cards. - Voit nyt käyttää Cockatricea juuri päivitetyillä korteilla. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Jos korttitietokannat eivät päivity automaattisesti, käynnistä Cockatrice uudelleen. - - - - SaveSetsPage - - - - Error - Virhe - - - - No set has been imported. - Yhtään settiä ei ladattu. - - - - Sets imported - Ladatut ekspansiot - - - - A cockatrice database file of %1 MB has been downloaded. - Cockatrice tietokantatiedosto kooltaan %1 MB on ladattu. - - - - The following sets have been found: - Seuraavat setit löydettiin: - - - - Press "Save" to store the imported cards in the Cockatrice database. - Paina "Tallenna" varastoidaksesi ladatut kortit Cockatricen tietokantaan. - - - - The card database will be saved at the following location: - Korttitietokanta tallennetaan seuraavaan sijaintiin: - - - - Save to a custom path (not recommended) - Tallenna mukautettuun sijaintiin (ei-suositeltu) - - - - &Save - &Tallenna - - - - Import finished: %1 cards. - Lataaminen valmis: %1 kortit. - - - - %1: %2 cards imported - %1: %2 kortit ladattu - - - - Save card database - Tallenna korttitietokanta - - - - XML; card database (*.xml) - XML; korttitietokanta (*.xml) - - - - The file could not be saved to %1 - Tiedostoa ei voitu tallentaa osoitteeseen %1 - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - Virhe - - - - The provided URL is not valid: - - - - - Downloading (0MB) - Ladataan (0 MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Ladataan (%1 MB) - - - - Network error: %1. - Yhteysvirhe: %1. - - - - The file could not be saved to %1 - Tiedostoa ei voitu tallentaa osoitteeseen %1 - - - - UnZip - - - ZIP operation completed successfully. - ZIP-operaatio suoritettiin onnistuneesti. - - - - Failed to initialize or load zlib library. - zlib-kirjaston alustaminen tai lataaminen epäonnistui - - - - zlib library error. - zlib-kirjastovirhe. - - - - Unable to create or open file. - Tiedostoa ei voitu luoda tai avata. - - - - Partially corrupted archive. Some files might be extracted. - Osittain korruptoitunut tiedosto. Osia zip-tiedostosta saatettiin purkaa. - - - - Corrupted archive. - Korruptoitunut tiedosto. - - - - Wrong password. - Väärä salasana. - - - - No archive has been created yet. - Yhtään arkistoa ei olla vielä luotu. - - - - File or directory does not exist. - Tiedostoa tai hakemistoa ei ole olemassa. - - - - File read error. - Tiedostonlukuvirhe. - - - - File write error. - Tiedostonkirjoitusvirhe. - - - - File seek error. - Tiedostonetsimisvirhe. - - - - Unable to create a directory. - Hakemistoa ei voitu luoda. - - - - Invalid device. - Virheellinen laite. - - - - Invalid or incompatible zip archive. - Virheellinen tai yhteensopimaton zip-tiedosto. - - - - Inconsistent headers. Archive might be corrupted. - Epäjohdonmukaiset otsikot. Tiedosto saattaa olla korruptoitunut. - - - - Unknown error. - Tuntematon virhe. - - - - Zip - - - ZIP operation completed successfully. - ZIP-operaatio suoritettiin onnistuneesti. - - - - Failed to initialize or load zlib library. - zlib-kirjaston alustaminen tai lataaminen epäonnistui. - - - - zlib library error. - zlib-kirjastovirhe. - - - - Unable to create or open file. - Tiedostoa ei voitu luoda tai avata. - - - - No archive has been created yet. - Yhtään arkistoa ei olla vielä luotu. - - - - File or directory does not exist. - Tiedostoa tai hakemistoa ei ole olemassa. - - - - File read error. - Tiedostonlukuvirhe. - - - - File write error. - Tiedostonkirjoitusvirhe. - - - - File seek error. - Tiedostonetsimisvirhe. - - - - Unknown error. - Tuntematon virhe. - - - - i18n - - - English - Suomi (Finnish) - - - - main - - - Only run in spoiler mode - Suorita vain spoileritilassa - - - - Run in no-confirm background mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_fr.ts b/oracle/translations/oracle_fr.ts index 47ab5d0fa..28bf5d68b 100644 --- a/oracle/translations/oracle_fr.ts +++ b/oracle/translations/oracle_fr.ts @@ -1,532 +1,337 @@ - + IntroPage - Introduction Introduction - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Cet assistant va importer la liste des éditions, cartes et jetons qui seront utilisés par Cockatrice. + English + Français (French) - - Interface language: - Langage de l'interface : + Language: + Langue: - - Version: - Version : + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + Cet assistant va importer la liste des éditions, cartes et jetons qui seront utilisés par Cockatrice. +Vous devrez spécifier une URL ou un fichier qui sera utilisé comme source. LoadSetsPage - Source selection Choix du fichier source - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Veuillez spécifier une source compatible pour la liste d'éditions et de cartes. Vous pouvez spécifier une URL qui sera utilisée pour télécharger un fichier ou utiliser un fichier existant sur votre ordinateur. - - - - Download URL: - URL de téléchargement : - - - - Local file: - Fichier local : - - - - Restore default URL - Restaurer l'URL par défaut - - - - Choose file... - Choisissez un fichier... - - - - Load sets file - Charger une liste d'éditions - - - - Sets file (%1) - Sets JSON file (%1) - Choisir le fichier (%1) - - - - - - - - - - Error - Erreur - - - - The provided URL is not valid. - L'URL fournie n'est pas valable - - - - Downloading (0MB) - Téléchargement en cours (0MB) - - - - Please choose a file. - Choisissez un fichier. - - - - Cannot open file '%1'. - Impossible d'ouvrir le fichier '%1'. - - - - Downloading (%1MB) - Téléchargement (%1MB) - - - - Network error: %1. - Erreur réseau : %1. - - - - Parsing file - Traitement du fichier. - - - - Xz extraction failed. - L'extraction du zip à échoué. - - - - Sorry, this version of Oracle does not support xz compressed files. - Désolé, cette version d'Oracle ne supporte pas les fichiers zip. - - - - Failed to open Zip archive: %1. - Impossible d'ouvrir l'archive zip: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Extraction zip échouée: l'archive zip contient plus qu'un fichier. - - - - Zip extraction failed: %1. - L'extraction du zip a échoué : %1. - - - - Sorry, this version of Oracle does not support zipped files. - Désolé, cette version d'Oracle ne supporte pas les fichiers zip. - - - - Failed to interpret downloaded data. - Échec lors de l'interprétation des données téléchargées. - - - - Do you want to download the uncompressed file instead? - Voulez-vous télécharger le fichier non compressé à la place ? - - - - The file was retrieved successfully, but it does not contain any sets data. - Le fichier a été trouvé, mais ne contient aucune éditions. - - - - LoadSpoilersPage - - - Save spoiler database - Enregistrer la base de données des spoilers - - - - XML; spoiler database (*.xml) - XML ; base de données des spoilers (*.xml) - - - - spoiler - spoiler - - - - Spoilers import - Importation des spoilers - - - - Please specify a compatible source for spoiler data. - Veuillez spécifier une source compatible pour les spoilers. - - - - Download URL: - URL de téléchargement : - - - Local file: Fichier local: - - Restore default URL - Restaurer l'URL par défaut - - - Choose file... Choisissez un fichier... - - The spoiler database will be saved at the following location: - La base de données des spoilers sera enregistrée à l'emplacement suivant : + Load sets file + Charger une liste d'éditions - - Save to a custom path (not recommended) - Sauvegarder à un emplacement personnalisé (non recommandé) + Sets JSON file (*.json *.zip) + Fichier d'éditions JSON (*.json *.zip) + + + Sets JSON file (*.json) + Fichier d'éditions JSON (*.json) + + + Error + Erreur + + + Downloading (0MB) + Téléchargement (0MB) + + + Please choose a file. + Choisissez un fichier. + + + Cannot open file '%1'. + Impossible d'ouvrir le fichier '%1'. + + + Downloading (%1MB) + Téléchargement (%1MB) + + + Network error: %1. + Erreur réseau : %1. + + + Parsing file + Traitement du fichier. + + + Failed to open Zip archive: %1. + Impossible d'ouvrir l'archive zip: %1. + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + Extraction zip échouée: l'archive zip contient plus qu'un fichier. + + + Zip extraction failed: %1. + L'extraction du zip a échoué : %1. + + + Sorry, this version of Oracle does not support zipped files. + Désolé, cette version d'Oracle ne supporte pas les fichiers zip. + + + Do you want to try to download a fresh copy of the uncompressed file instead? + Voulez vous essayer de télécharger une copie non compressée de fichier à la place ? + + + The file was retrieved successfully, but it does not contain any sets data. + Le fichier a été trouvé, mais ne contient aucune éditions. + + + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + Spécifiez une source pour la liste des éditions et des cartes. Vous pouvez spécifier une URL qui sera téléchargée ou utiliser un fichier existant sur votre ordinateur. + + + Download URL: + URL de téléchargement: + + + Restore default URL + Restaurer l'URL par défaut + + + The provided URL is not valid. + L'URL fournie n'est pas valable LoadTokensPage - - Save token database - Enregistrer la base de données des jetons + Tokens source selection + sélection de la source des jetons - - XML; token database (*.xml) - XML ; base de données des jetons (*.xml) + Error + Erreur - - tokens - jetons + Downloading (0MB) + Téléchargement (0MB) - - Tokens import - Importation des jetons + Downloading (%1MB) + Téléchargement (%1MB) - - Please specify a compatible source for token data. - Veuillez spécifier une source compatible pour les jetons. + Network error: %1. + Erreur réseau : %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + Spécifiez une source pour la liste des jetons. Vous pouvez spécifier une URL qui sera téléchargée ou utiliser un fichier existant sur votre ordinateur. - Download URL: URL de téléchargement: - - Local file: - Ficher local: - - - Restore default URL - Restaurer l'URL par défaut + Restorer l'URL par défaut - - Choose file... - Choisissez un ficher... - - - - The token database will be saved at the following location: - La base de données des jetons sera enregistrée à l'emplacement suivant : - - - - Save to a custom path (not recommended) - Sauvegarder à un emplacement personnalisé (non recommandé) + The provided URL is not valid. + L'URL fournie n'est pas valide. OracleImporter - Dummy set containing tokens - Fausse édition contenant les jetons + Fausse édition contenant les jetons. OracleWizard - Oracle Importer - Importateur Oracle - - - - OutroPage - - - Finished - Terminé + Oracle Importeur - - The wizard has finished. - L'assistant a terminé. - - - - You can now start using Cockatrice with the newly updated cards. - Vous pouvez maintenant commencer à utiliser Cockatrice avec les cartes mises à jour. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Si les bases de données de cartes ne rechargent pas automatiquement, redémarrez le client Cockatrice. + Save + Sauvegarder SaveSetsPage - - - Error - Erreur - - - - No set has been imported. - Aucune édition n'a été importé. - - - Sets imported Éditions importées - - A cockatrice database file of %1 MB has been downloaded. - Un fichier de base de données Cockatrice de %1 MB a été téléchargé. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + Les éditions suivantes ont été ajoutées. Appuyez sur "Sauvegarder" pour enregister les cartes importées dans la base de cockatrice. - - The following sets have been found: - Les éditions suivantes ont été trouvées : + Save to the default path (recommended) + Sauvergarder au chemin par défaut (recommendé) - - Press "Save" to store the imported cards in the Cockatrice database. - Cliquez sur « Enregistrer » pour enregistrer les cartes importées dans la base de données de Cockatrice. + Error + Erreur - - The card database will be saved at the following location: - La base de données des cartes sera enregistrée à l'emplacement suivant : + No set has been imported. + Aucune édition n'a été importé. - - Save to a custom path (not recommended) - Sauvegarder à un emplacement personnalisé (non recommandé) - - - - &Save - Sauvegarder - - - Import finished: %1 cards. Import terminé: %1 cartes. - %1: %2 cards imported %1: %2 cartes ajoutées. - Save card database Sauvegarder la base de carte - XML; card database (*.xml) - XML ; base de données de cartes (*.xml) + XML; base de donnée de carte (*.xml) + + + Success + Importation réussi + + + The card database has been saved successfully to +%1 + La base de donnée de carte à été correctement sauvegardée dans +%1 - The file could not be saved to %1 Le fichier n'a pu être sauvegarder au chemin '%1' - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - Chargez le fichier %1 + Tokens imported + Jetons importés - - %1 file (%1) - fichier %1 (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Les jetons ont été importés. Pressez sur "Sauvegarder" pour sauver les jetons importés dans la base de données des jetons Cockatrice. + + + Save to the default path (recommended) + Sauvergarder au chemin par défaut (recommendé) + + + Save token database + Sauvegarder la base des jetons + + + XML; token database (*.xml) + XML; bases de données des jetons (*.xml) + + + Success + Réussite + + + The token database has been saved successfully to +%1 + La base de donnée de jetons a été correctement sauvegardée dans %1 - - - - - Error - Erreur + Érreur - - The provided URL is not valid: - L'URL fournie n'est pas valide : - - - - Downloading (0MB) - Téléchargement (0 Mo) - - - - Please choose a file. - Merci de choisir un fichier. - - - - Cannot open file '%1'. - Le fichier '%1' ne peut pas être ouvert. - - - - Downloading (%1MB) - Téléchargement (%1 Mo) - - - - Network error: %1. - Erreur réseau : %1. - - - The file could not be saved to %1 - Le fichier n'a pas pu être enregistré dans %1 + Le fichier n'a pas pu être sauvegardé dans %1 UnZip - ZIP operation completed successfully. - Opération ZIP complétée avec succès. + Extraction zip réussi. - Failed to initialize or load zlib library. Impossible d'initialiser ou de charger la bibliothèque zlib. - zlib library error. Erreur avec la bibliothèque zlib. - Unable to create or open file. - Impossible de créer ou ouvrir le fichier. + Impossible de créer ou d'ouvrir le fichier - Partially corrupted archive. Some files might be extracted. Archive partiellement corrompue. Certains fichiers ont pu être extraits. - Corrupted archive. - Archive corrompue. + Archive corrompu. - Wrong password. Mauvais mot de passe. - No archive has been created yet. Aucune archive n'a été créée pour l'instant. - File or directory does not exist. Le fichier ou le dossier n'existe pas. - File read error. Erreur lors de la lecture du fichier. - File write error. Erreur lors de l'écriture du fichier. - File seek error. - Erreur lors de la recherche dans le fichier. + File seek error. - Unable to create a directory. - Impossible de créer un répertoire. + Impossible de créer le répertoire - Invalid device. Périphérique non valide. - Invalid or incompatible zip archive. Archive zip non valide ou incompatible. - Inconsistent headers. Archive might be corrupted. - En-têtes inconsistants. L'archive peut être corrompue. + En-têtes inconsistants. L'archive peut être corrompu. - Unknown error. Erreur inconnue. @@ -534,75 +339,44 @@ Zip - ZIP operation completed successfully. - Opération ZIP complétée avec succès. + Extraction zip réussi. - Failed to initialize or load zlib library. - Impossible d'initialiser ou de charger la bibliothèque zlib. + Impossible d'initialiser ou charger la bibliothèque zlib. - zlib library error. Erreur avec la bibliothèque zlib. - Unable to create or open file. - Impossible de créer ou d'ouvrir le fichier. + Impossible de créer ou d'ouvrir le fichier - No archive has been created yet. Aucune archive n'a été créée pour l'instant. - File or directory does not exist. Le fichier ou le dossier n'existe pas. - File read error. Erreur lors de la lecture du fichier. - File write error. Erreur lors de l'écriture du fichier. - File seek error. - Erreur lors de la recherche dans le fichier. + File seek error. - Unknown error. Erreur inconnue. - - i18n - - - English - Français (French) - - - - main - - - Only run in spoiler mode - Ne fonctionne qu'en mode spoiler - - - - Run in no-confirm background mode - S'exécuter en mode tâche de fond sans demande de confirmation - - \ No newline at end of file diff --git a/oracle/translations/oracle_hu.ts b/oracle/translations/oracle_hu.ts deleted file mode 100644 index aed051595..000000000 --- a/oracle/translations/oracle_hu.ts +++ /dev/null @@ -1,398 +0,0 @@ - - - IntroPage - - - Introduction - - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - - - - - Interface language: - - - - - Version: - - - - - LoadSetsPage - - - Source selection - - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - Load sets file - - - - - Sets JSON file (%1) - - - - - - - - - - Error - - - - - The provided URL is not valid. - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - Parsing file - - - - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - - Failed to open Zip archive: %1. - - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - - - - - Zip extraction failed: %1. - - - - - Sorry, this version of Oracle does not support zipped files. - - - - - Do you want to download the uncompressed file instead? - - - - - The file was retrieved successfully, but it does not contain any sets data. - - - - - LoadSpoilersPage - - - Save spoiler database - - - - - XML; spoiler database (*.xml) - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - - Download URL: - - - - - Restore default URL - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - LoadTokensPage - - - Save token database - - - - - XML; token database (*.xml) - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - - Download URL: - - - - - Restore default URL - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - OracleImporter - - - Dummy set containing tokens - - - - - OracleWizard - - - Oracle Importer - - - - - OutroPage - - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - - - - - SaveSetsPage - - - - Error - - - - - No set has been imported. - - - - - Sets imported - - - - - The following sets have been found: - - - - - Press "Save" to store the imported cards in the Cockatrice database. - - - - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - - - - - %1: %2 cards imported - - - - - Save card database - - - - - XML; card database (*.xml) - - - - - The file could not be saved to %1 - - - - - SimpleDownloadFilePage - - - - - Error - - - - - The provided URL is not valid. - - - - - Downloading (0MB) - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - The file could not be saved to %1 - - - - - i18n - - - English - Magyar (Hungarian) - - - - main - - - Only run in spoiler mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_it.ts b/oracle/translations/oracle_it.ts index 34bcb3cbc..f4f907982 100644 --- a/oracle/translations/oracle_it.ts +++ b/oracle/translations/oracle_it.ts @@ -1,283 +1,161 @@ - + IntroPage - Introduction Introduzione - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Questo wizard importerà la lista di set, carte -e pedine che verranno usate da Cockatrice. + English + Italiano (Italian) - - Interface language: - Lingua dell'interfaccia: + Language: + Lingua: - - Version: - Versione: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + Questo wizard importerà la lista dei set e delle carte che verranno usate da Cockatrice.<br/>Dovrai specificare un indirizzo url o il nome di un file che verrà utilizzato come sorgente. LoadSetsPage - Source selection Selezione sorgente - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Specifica una sorgente compatibile per la lista dei set e delle carte. Puoi specificare un indirizzo URL da cui scaricare il file o selezionare un file già presente nel tuo computer. - - - - Download URL: - Indirizzo download: - - - Local file: File nel pc: - - Restore default URL - Usa l'indirizzo predefinito - - - Choose file... Scegli file... - Load sets file Carica file dei set - - Sets file (%1) - Sets JSON file (%1) - File dei set (%1) + Sets JSON file (*.json *.zip) + File dei set JSON (*.json *.zip) + + + Sets JSON file (*.json) + File set set JSON (*.json) - - - - - - - Error Errore - - The provided URL is not valid. - L'indirizzo specificato non è valido. - - - Downloading (0MB) Scaricamento (0MB) - Please choose a file. - Seleziona un file. + Selezina un file. - Cannot open file '%1'. Impossibile aprire il file '%1'. - Downloading (%1MB) Scaricamento (%1MB) - Network error: %1. Errore di rete: %1 - Parsing file Analisi dei file - - Xz extraction failed. - Estrazione da file xz fallita. - - - - Sorry, this version of Oracle does not support xz compressed files. - Spiacente, ma questa versione di Oracle non supporta i file xz. - - - Failed to open Zip archive: %1. Impossibile aprire il file Zip: %1 - Zip extraction failed: the Zip archive doesn't contain exactly one file. Estrazione file Zip fallita: lo Zip non contiene un solo file. - Zip extraction failed: %1. Estrazione file Zip fallita: %1 - Sorry, this version of Oracle does not support zipped files. - Spiacente, ma questa versione di Oracle non supporta i file zip. + Spiacente, ma questa versione di Oracle non supporta in file zippati. - - Failed to interpret downloaded data. - Impossibile interpretare i dati scaricati. + Do you want to try to download a fresh copy of the uncompressed file instead? + Vuoi provare a riscaricare un copia pulita del file non zippato? - - Do you want to download the uncompressed file instead? - Vuoi provare a scaricare il file non compresso? - - - The file was retrieved successfully, but it does not contain any sets data. Il file è stato analizzato correttamente, ma non contiene i dati di nessun set. - - - LoadSpoilersPage - - Save spoiler database - Salva archivio spoiler + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + Specifica una sorgente per la lista dei set e delle carte. Puoi specificare un indirizzo url da cui scaricare il file o alternativamente usare un file già presente nel tuo computer. - - XML; spoiler database (*.xml) - XML; archivio spoiler (*.xml) - - - - spoiler - spoiler - - - - Spoilers import - Importazione spoiler - - - - Please specify a compatible source for spoiler data. - Specifica una sorgente compatibile per gli spoiler. - - - Download URL: Indirizzo download: - - Local file: - File nel pc: - - - Restore default URL Usa l'indirizzo predefinito - - Choose file... - Scegli file... - - - - The spoiler database will be saved at the following location: - L'archivio degli spoiler verrà salvato nel seguente percorso: - - - - Save to a custom path (not recommended) - Salva in un percorso diverso (sconsigliato) + The provided URL is not valid. + L'indirizzo specificato non è valido. LoadTokensPage - - Save token database - Salva archivio pedine + Tokens source selection + Selezione sorgente pedine - - XML; token database (*.xml) - XML; archivio pedine (*.xml) + Error + Errore - - tokens - pedine + Downloading (0MB) + Scaricamento (0MB) - - Tokens import - Importazione pedine + Downloading (%1MB) + Scaricamento (%1MB) - - Please specify a compatible source for token data. - Specifica una sorgente compatibile per le pedine. + Network error: %1. + Errore di rete: %1 + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + Specifica una sorgente per la lista delle pedine. Puoi specificare un indirizzo url da cui scaricare il file o alternativamente usare un file già presente nel tuo computer. - Download URL: Indirizzo download: - - Local file: - File nel pc: - - - Restore default URL Usa l'indirizzo predefinito - - Choose file... - Scegli file... - - - - The token database will be saved at the following location: - L'archivio delle pedine verrà salvato nel seguente percorso: - - - - Save to a custom path (not recommended) - Salva in un percorso diverso (sconsigliato) + The provided URL is not valid. + L'indirizzo specificato non è valido. OracleImporter - Dummy set containing tokens Set finto contenente i token @@ -285,161 +163,104 @@ e pedine che verranno usate da Cockatrice. OracleWizard - Oracle Importer Oracle Importer - - - OutroPage - - Finished - Finito - - - - The wizard has finished. - Il wizard è completato. - - - - You can now start using Cockatrice with the newly updated cards. - Adesso puoi iniziare ad usare Cockatrice con le nuove carte aggiornate. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Se il database delle carte non si ricarica in automatico, riavvia il programma Cockatrice. + Save + Salva SaveSetsPage - - - Error - Errore - - - - No set has been imported. - Nessun set importato. - - - Sets imported Set importati - - A cockatrice database file of %1 MB has been downloaded. - È stato scaricato un file database di Cockatrice di %1 MB. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + I seguenti set sono stati importati. Premi "Salva" per salvare le carte importate nell'archivio di Cockatrice. - - The following sets have been found: - Sono stati trovati i seguenti set: + Save to the default path (recommended) + Salva nel percorso predefinito (raccomandato) - - Press "Save" to store the imported cards in the Cockatrice database. - Premi "Salva" per salvare le carte importate nel database di Cockatrice. + Error + Errore - - The card database will be saved at the following location: - L'archivio delle carte verrà salvato nel seguente percorso: + No set has been imported. + Nessun set importato. - - Save to a custom path (not recommended) - Salva in un percorso diverso (sconsigliato) - - - - &Save - &Salva - - - Import finished: %1 cards. Importazione conclusa: %1 carte. - %1: %2 cards imported %1: %2 carte importate - Save card database Salva archivio carte - XML; card database (*.xml) XML; archivio carte (*.xml) - + Success + Successo + + + The card database has been saved successfully to +%1 + L'archivio delle carte è stato salvato correttamente su +%1 + + The file could not be saved to %1 Impossibile salvare il file su %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - Carica %1 file + Tokens imported + Pedine importate - - %1 file (%1) - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + Le pedine sono state importate. Premi "Salva" per salvare le pedine importate nell'archivio delle pedine di Cockatrice. + + + Save to the default path (recommended) + Salva nel percorso predefinito (raccomandato) + + + Save token database + Salva archivio pedine + + + XML; token database (*.xml) + XML; archivio pedine (*.xml) + + + Success + Successo + + + The token database has been saved successfully to +%1 + L'archivio delle pedine è stato salvato correttamente su +%1 - - - - - Error Errore - - The provided URL is not valid: - L'indirizzo fornito non è valido: - - - - Downloading (0MB) - Scaricamento (0MB) - - - - Please choose a file. - Seleziona un file. - - - - Cannot open file '%1'. - Impossibile aprire il file '%1'. - - - - Downloading (%1MB) - Scaricamento (%1MB) - - - - Network error: %1. - Errore di rete: %1. - - - The file could not be saved to %1 Impossibile salvare il file su %1 @@ -447,87 +268,70 @@ e pedine che verranno usate da Cockatrice. UnZip - ZIP operation completed successfully. Operazione ZIP completata con successo. - Failed to initialize or load zlib library. - Impossibile inizializzare o caricare libreria zlib. + Impossibile caricare le libreria zlib. - zlib library error. - Errore libreria zlib. + errore libreria zlib. - Unable to create or open file. - Impossibile creare o aprile il file. + Impossibile creare o aprire il file. - Partially corrupted archive. Some files might be extracted. - Archivio parzialmente corrotto. Alcuni file potrebbero essere estratti. + Archivio parzialmente danneggiato. Alcuni file potrebbero essere stati estratti. - Corrupted archive. - Archivio corrotto. + Archivio danneggiato. - Wrong password. - Password errata. + Password sbagliata. - No archive has been created yet. - Nessun archivio è stato ancora creato. + L'archivio non è ancora stato creato. - File or directory does not exist. - Il file o il percorso non esistono. + Il file o la cartella non esiste. - File read error. - Errore di lettura file. + Errore di lettura del file. - File write error. - Errore di scrittura file. + Errore di scrittura del file. - File seek error. - Errore di ricerca file. + Errore di ricerca nel file. - Unable to create a directory. - Impossibile creare il percorso specificato. + Impossibile creare una cartella. - Invalid device. - Dispositivo invalido. + Dispositivo non valido. - Invalid or incompatible zip archive. - Archivio ZIP invalido o incompatibile. + Archivio zip non valido o incompatibile. - Inconsistent headers. Archive might be corrupted. - Header inconsistenti. L'archivio potrebbe essere corrotto. + Intestazioni inconsistenti. L'archivio potrebbe essere danneggiato. - Unknown error. Errore sconosciuto. @@ -535,75 +339,44 @@ e pedine che verranno usate da Cockatrice. Zip - ZIP operation completed successfully. Operazione ZIP completata con successo. - Failed to initialize or load zlib library. - Inizializzazione o caricamento libreria zlib non riusciti. + Impossibile caricare le libreria zlib. - zlib library error. - Errore libreria zlib. + errore libreria zlib. - Unable to create or open file. Impossibile creare o aprire il file. - No archive has been created yet. - Nessun archivio è stato ancora creato. + L'archivio non è ancora stato creato. - File or directory does not exist. - Il file o il percorso non esistono. + Il file o la cartella non esiste. - File read error. - Errore di lettura file. + Errore di lettura del file. - File write error. - Errore di scrittura file. + Errore di scrittura del file. - File seek error. - Errore di ricerca file. + Errore di ricerca nel file. - Unknown error. Errore sconosciuto. - - i18n - - - English - Italiano (Italian) - - - - main - - - Only run in spoiler mode - Avvia solo in modalità spoiler - - - - Run in no-confirm background mode - Esegui in background senza conferma - - \ No newline at end of file diff --git a/oracle/translations/oracle_ja.ts b/oracle/translations/oracle_ja.ts index 8319f37a2..dec2f373d 100644 --- a/oracle/translations/oracle_ja.ts +++ b/oracle/translations/oracle_ja.ts @@ -1,282 +1,162 @@ - + IntroPage - Introduction はじめに - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - このウィザードでは、Cockatriceで使用されるカードやトークン、セットのリストをインポートします。 + English + 日本語 (Japanese) - - Interface language: - インターフェース言語: + Language: + 言語: - - Version: - バージョン: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + このウィザードでは、Cockatriceが使用するカードセットやトークンのリストをインポートします。 +ソースとして使用するURLまたはファイルを指定した後、利用可能リストから入れたいセットを選択します。 LoadSetsPage - Source selection ソース選択 - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - セットとカードのリストの互換性のあるソースを指定してください。ダウンロードするURLアドレスを指定するか、コンピューターから既存のファイルを使用できます。 - - - - Download URL: - ダウンロードURL: - - - Local file: ローカルファイル: - - Restore default URL - デフォルトのURLを復元 - - - Choose file... ファイルを選択... - Load sets file カードセットファイルを開く - - Sets file (%1) - Sets JSON file (%1) - + Sets JSON file (*.json *.zip) + カードセット JSON ファイル (*.json *.zip) + + + Sets JSON file (*.json) + カードセット JSON ファイル (*.json) - - - - - - - Error エラー - - The provided URL is not valid. - 指定されたURLは無効です。 - - - Downloading (0MB) ダウンロード中 (0MB) - Please choose a file. ファイルを選択してください。 - Cannot open file '%1'. '%1'を開けませんでした。 - Downloading (%1MB) ダウンロード中 (%1MB) - Network error: %1. ネットワークエラー: %1。 - Parsing file ファイルの解析 - - Xz extraction failed. - Xz展開に失敗。 - - - - Sorry, this version of Oracle does not support xz compressed files. - このバージョンのOracleはxz圧縮ファイルをサポートしていません。 - - - Failed to open Zip archive: %1. ZIPアーカイブの展開に失敗: %1。 - Zip extraction failed: the Zip archive doesn't contain exactly one file. - ZIP展開に失敗:Zipアーカイブに含まれるファイルが1つだけではありません。 + ZIP展開に失敗:Zipアーカイブは、正確に一つのファイルが含まれていません。 - Zip extraction failed: %1. ZIP展開に失敗: %1。 - Sorry, this version of Oracle does not support zipped files. 申し訳ありませんが現バージョンのOracleはzip形式のファイルをサポートしていません。 - - Failed to interpret downloaded data. - + Do you want to try to download a fresh copy of the uncompressed file instead? + 代わりに非圧縮ファイルの新しいコピーをダウンロードしますか? - - Do you want to download the uncompressed file instead? - 代わりに非圧縮ファイルをダウンロードしますか? - - - The file was retrieved successfully, but it does not contain any sets data. - ファイルは正常に取得されましたが、カードセットのデータが含まれていませんでした。 - - - - LoadSpoilersPage - - - Save spoiler database - スポイラーデータベースを保存 + ファイルは正常に取得されたが、カードセットのデータが含まれていませんでした。 - - XML; spoiler database (*.xml) - XML; スポイラーデータベース (*.xml) + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + カードセットとカードのリストのソースファイルを指定してください。ダウンロード可能なURLか、コンピューターに保存したソースファイルを指定することができます(通常はデフォルトのURLからダウンロードで構いません)。 - - spoiler - - - - - Spoilers import - スポイラーインポート - - - - Please specify a compatible source for spoiler data. - 互換性のあるスポイラーデータのソースを指定してください。 - - - Download URL: ダウンロードURL: - - Local file: - - - - Restore default URL デフォルトのURLを復元 - - Choose file... - - - - - The spoiler database will be saved at the following location: - スポイラーデータベースは、以下の場所に保存されます: - - - - Save to a custom path (not recommended) - 別のパスに保存(非推奨) + The provided URL is not valid. + 指定されたURLは無効です。 LoadTokensPage - - Save token database - トークンデータベースを保存 + Tokens source selection + トークンのソース選択 - - XML; token database (*.xml) - XML; トークンデータベース (*.xml) + Error + エラー - - tokens - + Downloading (0MB) + ダウンロード中 (0MB) - - Tokens import - トークンのインポート + Downloading (%1MB) + ダウンロード中 (%1MB) - - Please specify a compatible source for token data. - トークンデータのソースを選択してください。 + Network error: %1. + ネットワークエラー: %1。 + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + トークンのソースファイルを指定してください。ダウンロード可能なURLか、コンピューターに保存したソースファイルを指定することができます(通常はデフォルトのURLからダウンロードで構いません)。 - Download URL: ダウンロードURL: - - Local file: - - - - Restore default URL デフォルトのURLを復元 - - Choose file... - - - - - The token database will be saved at the following location: - トークンデータベースは以下の場所に保存されます。 - - - - Save to a custom path (not recommended) - 別のパスに保存(非推奨) + The provided URL is not valid. + 指定されたURLは無効です。 OracleImporter - Dummy set containing tokens ダミーセットを含むトークン @@ -284,161 +164,104 @@ OracleWizard - Oracle Importer - Oracle Importer - オラクル・インポーター - - - - OutroPage - - - Finished - 完了しました! + Oracle Importer - - The wizard has finished. - ウィザードが完了しました。 - - - - You can now start using Cockatrice with the newly updated cards. - Cockatriceで新しく更新されたカードを使うことが出来ます。 - - - - If the card databases don't reload automatically, restart the Cockatrice client. - カードデータベースが自動的に再読込されない場合は、Cockatriceを再起動して下さい。 + Save + 保存 SaveSetsPage - - - Error - エラー - - - - No set has been imported. - セットはインポートされませんでした。 - - - Sets imported カードセットインポート - - A cockatrice database file of %1 MB has been downloaded. - + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + 以下のカードセットがインポートされます。”保存”をクリックするとインポートしたカードをCockatriceデータベースに保存します。 - - The following sets have been found: - 次のセットが見つかりました: + Save to the default path (recommended) + デフォルトのパスに保存 (推奨) - - Press "Save" to store the imported cards in the Cockatrice database. - ”保存”をクリックするとインポートしたカードをCockatriceデータベースに保存します。 + Error + エラー - - The card database will be saved at the following location: - カードデータベースは以下の場所に保存されます: + No set has been imported. + セットはインポートされませんでした。 - - Save to a custom path (not recommended) - 別のパスに保存(非推奨) - - - - &Save - 保存 - - - Import finished: %1 cards. %1枚のカードがインポートされました。 - %1: %2 cards imported %1: %2枚のカードがインポートされました。 - Save card database カードデータベースを保存 - XML; card database (*.xml) XML; card database (*.xml) - + Success + 完了 + + + The card database has been saved successfully to +%1 + カードデータベースは以下に保存されました: +%1 + + The file could not be saved to %1 %1に保存できませんでした。 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - + Tokens imported + トークンインポート - - %1 file (%1) - + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + トークンがインポートされました。”保存”をクリックするとインポートしたトークンをCockatriceデータベースに保存します。 + + + Save to the default path (recommended) + デフォルトのパスに保存 (推奨) + + + Save token database + トークンデータベースを保存 + + + XML; token database (*.xml) + XML; token database (*.xml) + + + Success + 完了 + + + The token database has been saved successfully to +%1 + トークンデータベースは以下に保存されました: +%1 - - - - - Error エラー - - The provided URL is not valid: - - - - - Downloading (0MB) - ダウンロード中 (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - ダウンロード中 (%1MB) - - - - Network error: %1. - ネットワークエラー: %1。 - - - The file could not be saved to %1 %1に保存できませんでした。 @@ -446,87 +269,70 @@ UnZip - ZIP operation completed successfully. 正常に完了しました。 - Failed to initialize or load zlib library. 初期化またはzlibライブラリのロードに失敗しました。 - zlib library error. zlibライブラリエラー。 - Unable to create or open file. ファイルが作成または開けませんでした。 - Partially corrupted archive. Some files might be extracted. アーカイブが部分的に破損しています。いくつかのファイルが抽出されることがあります。 - Corrupted archive. 破損したアーカイブ。 - Wrong password. パスワードが間違っています。 - No archive has been created yet. アーカイブは作成されませんでした。 - File or directory does not exist. ファイルまたはフォルダが存在しません。 - File read error. ファイル読み込みエラー。 - File write error. ファイル書き込みエラー。 - File seek error. シーク エラー。 - Unable to create a directory. フォルダが作成できませんでした。 - Invalid device. 無効なデバイス。 - Invalid or incompatible zip archive. 無効なまたは互換性のないzipアーカイブ。 - Inconsistent headers. Archive might be corrupted. 一貫性のないヘッダー。アーカイブが壊れている可能性があります。 - Unknown error. 不明なエラー。 @@ -534,75 +340,44 @@ Zip - ZIP operation completed successfully. 正常に完了しました。 - Failed to initialize or load zlib library. 初期化またはzlibライブラリのロードに失敗しました。 - zlib library error. zlibライブラリエラー。 - Unable to create or open file. ファイルが作成または開けませんでした。 - No archive has been created yet. アーカイブは作成されませんでした。 - File or directory does not exist. ファイルまたはフォルダが存在しません。 - File read error. ファイル読み込みエラー。 - File write error. ファイル書き込みエラー。 - File seek error. シーク エラー。 - Unknown error. 不明なエラー。 - - i18n - - - English - 日本語 (Japanese) - - - - main - - - Only run in spoiler mode - スポイラーモードでのみ起動 - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_ko.ts b/oracle/translations/oracle_ko.ts index c5c24583f..28d052364 100644 --- a/oracle/translations/oracle_ko.ts +++ b/oracle/translations/oracle_ko.ts @@ -1,282 +1,164 @@ - + IntroPage - Introduction 개요 - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - + English + 한국어 (Korean) - - Interface language: - + Language: + 언어 - - Version: - + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + 오라클은 코카트리스에서 사용할 확장판의 목록과 카드들의 정보를 갱신하는 프로그램입니다. +판본 목록 파일이 있는 웹 주소나 파일을 입력하신 후에 읽어온 목록에서 원하는 확장판을 선택해 불러올 수 있습니다. LoadSetsPage - Source selection 확장판 목록 파일 주소 입력 - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - 다운로드 주소: - - - Local file: 파일 위치: - - Restore default URL - 기본 주소로 복원 - - - Choose file... 파일 선택... - Load sets file 확장판 목록 파일 불러오기 - - Sets file (%1) - Sets JSON file (%1) - + Sets JSON file (*.json *.zip) + 확장판 목록 JSON 파일 (*.json *.zip) + + + Sets JSON file (*.json) + 확장판 목록 JSON 파일 (*.json) - - - - - - - Error 오류 - - The provided URL is not valid. - 잘못된 주소를 입력하셨습니다. - - - Downloading (0MB) 다운로드 중 (0MB) - Please choose a file. 확장판 목록 파일을 선택해 주세요. - Cannot open file '%1'. 파일 '%1'을(를) 열 수 없습니다. - Downloading (%1MB) 다운로드 중 (%1MB) - Network error: %1. 네트워크 오류 : %1. - Parsing file 목록 파싱중 - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. 압축파일 열기 실패 : %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. 압축 풀기 실패 : 압축 파일에 확장판 목록 파일 이외의 파일이 있습니다. - Zip extraction failed: %1. 압축 풀기 실패 : %1. - Sorry, this version of Oracle does not support zipped files. 죄송합니다. 본 버전에서는 압축 파일을 지원하지 않습니다. - - Failed to interpret downloaded data. - + Do you want to try to download a fresh copy of the uncompressed file instead? + 압축되지 않은 확장판 목록을 대신 내려받으시겠습니까? - - Do you want to download the uncompressed file instead? - - - - The file was retrieved successfully, but it does not contain any sets data. 파일을 성공적으로 다운로드 하였으나 확장판 정보가 들어있지 않습니다. - - - LoadSpoilersPage - - Save spoiler database - + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + 확장판 목록 및 카드 정보가 들어있는 파일의 위치를 입력해 주세요. +다운로드 할 수 있는 웹 주소나 컴퓨터에 저장되어 있는 파일을 선택 할 수 있습니다. - - XML; spoiler database (*.xml) - - - - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - + 웹 주소: - - Local file: - - - - Restore default URL - + 기본 주소로 복원 - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - + The provided URL is not valid. + 잘못된 주소를 입력하셨습니다. LoadTokensPage - - Save token database - + Tokens source selection + 토큰 파일 주소 입력 - - XML; token database (*.xml) - + Error + 오류 - - tokens - + Downloading (0MB) + 다운로드 중 (0MB) - - Tokens import - + Downloading (%1MB) + 다운로드 중 (%1MB) - - Please specify a compatible source for token data. - + Network error: %1. + 네트워크 오류 : %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + 토큰 목록 및 정보가 들어있는 파일의 위치를 입력해 주세요. +다운로드 할 수 있는 웹 주소나 컴퓨터에 저장되어 있는 파일을 선택 할 수 있습니다. - Download URL: 웹 주소: - - Local file: - - - - Restore default URL 기본 주소로 복원 - - Choose file... - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - + The provided URL is not valid. + 잘못된 주소를 입력하셨습니다. OracleImporter - Dummy set containing tokens 토큰 정보가 들어있는 더미 확장판 @@ -284,249 +166,177 @@ OracleWizard - Oracle Importer 오라클 - - - OutroPage - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - + Save + 저장 SaveSetsPage - - - Error - 오류 - - - - No set has been imported. - 아무 확장판도 불러오지 못했습니다. - - - Sets imported 확장판 불러오기 완료 - - A cockatrice database file of %1 MB has been downloaded. - + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + 아래와 같이 확장판을 불러왔습니다. +"저장" 버튼을 눌러 코카트리스에서 사용할 수 있는 카드 데이터베이스를 저장하실 수 있습니다. - - The following sets have been found: - + Save to the default path (recommended) + 기본 경로에 저장 (권장) - - Press "Save" to store the imported cards in the Cockatrice database. - + Error + 오류 - - The card database will be saved at the following location: - + No set has been imported. + 아무 확장판도 불러오지 못했습니다. - - Save to a custom path (not recommended) - - - - - &Save - - - - Import finished: %1 cards. 총 %1장의 카드 불러오기 완료 - %1: %2 cards imported %1에서 %2장의 카드 불러옴 - Save card database 카드 데이터베이스 저장 - XML; card database (*.xml) 카드 데이터베이스 XML 파일 (*.xml) - + Success + 성공 + + + The card database has been saved successfully to +%1 + 카드 데이터베이스를 다음 위치에 저장했습니다: +%1 + + The file could not be saved to %1 파일을 %1에 저장 할 수 없습니다. - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file - + Tokens imported + 토큰 불러오기 완료 - - %1 file (%1) - + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + 토큰 파일을 불러왔습니다. +"저장" 버튼을 누르면 코카트리스에서 토큰 파일을 불러옵니다. + + + Save to the default path (recommended) + 기본 경로에 저장 (권장) + + + Save token database + 토큰 파일 저장 + + + XML; token database (*.xml) + 토큰 정보 XML 파일 (*.xml) + + + Success + 성공 + + + The token database has been saved successfully to +%1 + 토큰 정보 파일을 다음 위치에 저장했습니다: +%1 - - - - - Error - + 오류 - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 - + 파일을 %1에 저장 할 수 없습니다. UnZip - ZIP operation completed successfully. 압축파일 작업을 성공적으로 완료하였습니다. - Failed to initialize or load zlib library. zlib 라이브러리를 초기화하거나 불러올 수 없습니다. - zlib library error. zlib 라이브러리 오류. - Unable to create or open file. 파일을 만들거나 열 수 없습니다. - Partially corrupted archive. Some files might be extracted. 압축파일의 일부가 손상되었습니다. 몇몇 파일은 압축이 풀렸을 수도 있습니다. - Corrupted archive. 압축파일이 손상되었습니다. - Wrong password. 비밀번호가 틀렸습니다. - No archive has been created yet. 압축파일이 아직 생성되지 않았습니다. - File or directory does not exist. 파일이나 디렉토리가 존재하지 않습니다. - File read error. 파일 읽기 오류. - File write error. 파일 쓰기 오류. - File seek error. 파일 찾기 오류. - Unable to create a directory. 디렉토리를 생성할 수 없었습니다. - Invalid device. 잘못된 장치입니다. - Invalid or incompatible zip archive. 잘못되거나 지원하지 않는 압축파일입니다. - Inconsistent headers. Archive might be corrupted. 헤더가 손상되었습니다. 압축 파일이 손상됐을 가능성이 있습니다. - Unknown error. 알 수 없는 오류. @@ -534,75 +344,44 @@ Zip - ZIP operation completed successfully. 압축파일 작업을 성공적으로 완료하였습니다. - Failed to initialize or load zlib library. zlib 라이브러리를 초기화하거나 불러올 수 없습니다. - zlib library error. zlib 라이브러리 오류. - Unable to create or open file. 파일을 만들거나 열 수 없습니다. - No archive has been created yet. 압축파일이 아직 생성되지 않았습니다. - File or directory does not exist. 파일이나 디렉토리가 존재하지 않습니다. - File read error. 파일 읽기 오류. - File write error. 파일 쓰기 오류. - File seek error. 파일 찾기 오류. - Unknown error. 알 수 없는 오류. - - i18n - - - English - 한국어 (Korean) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_nb.ts b/oracle/translations/oracle_nb.ts index b40868cc1..25457ec01 100644 --- a/oracle/translations/oracle_nb.ts +++ b/oracle/translations/oracle_nb.ts @@ -1,444 +1,264 @@ - + IntroPage - Introduction - Introduksjon - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - - Interface language: + English + (Norwegian Bokmål) + + + Language: - - Version: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. LoadSetsPage - Source selection - Kilde valg - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - Download URL: - Nedlastings URL - - - Local file: - Lokal fil + - - Restore default URL - Gjenopprett standard URL - - - Choose file... - Velg fil... + - Load sets file - Last set fil... - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + Error - Feil + - - The provided URL is not valid. - URL du anga er ikke gyldig. - - - Downloading (0MB) - Laster ned (0MB) + - Please choose a file. - Vennligst velg en fil + - Cannot open file '%1'. - Kunne ikke åpme '%1' + - Downloading (%1MB) - Laster ned (%1MB) + - Network error: %1. - Nettverks feil: %1 + - Parsing file - Tolker fil. - - - - Xz extraction failed. - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. - Kunne ikke åpne Zip Arkiv: %1 + - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip ekstraksjon feilet: Zip arkivet inneholder ikke nøyaktig en fil + - Zip extraction failed: %1. - Zip ekstraksjon feilet: %1 + - Sorry, this version of Oracle does not support zipped files. - Beklager, denne versjonen av Oracle støtter ikke Zip filer. - - - - Failed to interpret downloaded data. - - Do you want to download the uncompressed file instead? + Do you want to try to download a fresh copy of the uncompressed file instead? - The file was retrieved successfully, but it does not contain any sets data. - Filen ble hentet riktig, men den inneholder ikke noe set data - - - - LoadSpoilersPage - - - Save spoiler database - - XML; spoiler database (*.xml) + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - - Local file: - - - - Restore default URL - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error - - tokens + Downloading (0MB) - - Tokens import + Downloading (%1MB) - - Please specify a compatible source for token data. + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - Download URL: - Nedlastings URL - - - - Local file: - Restore default URL - Gjenopprett standard URL - - - - Choose file... - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens - Dummy sett som inneholder tokens + OracleWizard - Oracle Importer - Oracle importerer - - - - OutroPage - - - Finished - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. + Save SaveSetsPage - - - Error - Feil - - - - No set has been imported. - Ingen set har blitt importert. - - - Sets imported - Set importert - - - - A cockatrice database file of %1 MB has been downloaded. - - The following sets have been found: + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - Press "Save" to store the imported cards in the Cockatrice database. + Save to the default path (recommended) - - The card database will be saved at the following location: + Error - - Save to a custom path (not recommended) + No set has been imported. - - &Save - - - - Import finished: %1 cards. - Importering fullført: %1 kort. + - %1: %2 cards imported - %1: %2 kort importert. + - Save card database - Lagre kort database. + - XML; card database (*.xml) - XML; kort database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + - The file could not be saved to %1 - Filen kunne ikke lagres til %1 + - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 @@ -446,162 +266,114 @@ UnZip - ZIP operation completed successfully. - Zip operasjonen fullført. + - Failed to initialize or load zlib library. - Kunne ikke initialisere eller laste zlib mappen. + - zlib library error. - Feil med zlib mappen + - Unable to create or open file. - Kunne ikke lage eller åpne fil. + - Partially corrupted archive. Some files might be extracted. - Delvis korrupt arkiv. Noen filer kunne ikke bli ekstraktert. + - Corrupted archive. - Korrupt arkiv. + - Wrong password. - Feil passord + - No archive has been created yet. - Det har ikke blitt laget noe arkiv enda. + - File or directory does not exist. - Fil eller katalog eksisterer ikke. + - File read error. - Kunne ikke lese filen. + - File write error. - Kunne ikke skrive fil. + - File seek error. - Kunne ikke finne filen. + - Unable to create a directory. - Kunne ikke lage katalog. + - Invalid device. - Ugyldig apparat. + - Invalid or incompatible zip archive. - Ugyldig eller inkompatibelt zip akriv. + - Inconsistent headers. Archive might be corrupted. - Ukonsistente overskrifter. Arkivet kan være korrupt. + - Unknown error. - Ukjent feil. + Zip - ZIP operation completed successfully. - Zip operasjonen fullført. - - - - Failed to initialize or load zlib library. - Kunne ikke initialisere eller laste zlib biblioteket. - - - - zlib library error. - Feil med zlib mappen - - - - Unable to create or open file. - Kunne ikke lage eller åpne fil. - - - - No archive has been created yet. - Det har ikke blitt laget noe arkiv enda. - - - - File or directory does not exist. - Fil eller katalog eksisterer ikke. - - - - File read error. - Kunne ikke lese filen. - - - - File write error. - Kunne ikke skrive fil. - - - - File seek error. - Kunne ikke finne filen. - - - - Unknown error. - Ukjent feil. - - - - i18n - - - English - Norsk Bokmål (Norwegian Bokmål) - - - - main - - - Only run in spoiler mode - - Run in no-confirm background mode + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. diff --git a/oracle/translations/oracle_nl.ts b/oracle/translations/oracle_nl.ts index ed9a5fae9..acfc8cc05 100644 --- a/oracle/translations/oracle_nl.ts +++ b/oracle/translations/oracle_nl.ts @@ -1,282 +1,161 @@ - + IntroPage - Introduction Introductie - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Deze wizard importeert de lijst met sets, kaarten en tokens die door Cockatrice zullen worden gebruikt. + English + Nederlands (Dutch) - - Interface language: - Interface taal: + Language: + Taal: - - Version: - Versie: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + LoadSetsPage - Source selection Bron selectie - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Gelieve een compatibele bron te vermelden voor de lijst van sets en kaarten. U kunt een URL-adres opgeven dat wordt gedownload of een bestaand bestand van uw computer gebruiken. - - - - Download URL: - Download URL: - - - Local file: Lokaal bestand - - Restore default URL - Herstel standaard URL - - - Choose file... Bestand kiezen... - Load sets file Bestand laden - - Sets file (%1) - Sets JSON file (%1) - Sets bestand (%1) + Sets JSON file (*.json *.zip) + JSON sets bestand (*.json, *.zip) + + + Sets JSON file (*.json) + JSON sets bestand (*.json) - - - - - - - Error Fout - - The provided URL is not valid. - De ingevoerde URL is niet geldig. - - - Downloading (0MB) Downloaden (0MB) - Please choose a file. Gelieve een bestand te kiezen. - Cannot open file '%1'. Bestand '%1' kan niet geopend worden. - Downloading (%1MB) Downloaden (%1MB) - Network error: %1. Netwerk fout: %1. - Parsing file Parsen van bestand - - Xz extraction failed. - Xz extractie mislukt. - - - - Sorry, this version of Oracle does not support xz compressed files. - Sorry, deze versie van Oracle ondersteunt geen xz gecomprimeerde bestanden. - - - Failed to open Zip archive: %1. Zip archief kan niet geopend worden: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. Uitpakken van Zip niet gelukt: het archief moet precies één bestand bevatten. - Zip extraction failed: %1. Uitpakken van Zip niet gelukt: %1. - Sorry, this version of Oracle does not support zipped files. Sorry, deze versie van Oracle ondersteunt geen gecomprimeerde bestanden. - - Failed to interpret downloaded data. - Lezen van gedownloade data mislukt. + Do you want to try to download a fresh copy of the uncompressed file instead? + Wilt u in plaats daarvan een nieuwe kopie van de ongecomprimeerde bestanden downloaden? - - Do you want to download the uncompressed file instead? - Wilt u het ongecomprimeerde bestand downloaden? - - - The file was retrieved successfully, but it does not contain any sets data. Het bestand is succesvol binnengehaald, maar bevat geen set data. - - - LoadSpoilersPage - - Save spoiler database - Opslaan spoiler database - - - - XML; spoiler database (*.xml) - XML; spoiler database (*.xml) - - - - spoiler + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - Spoilers import - Spoilers import - - - - Please specify a compatible source for spoiler data. - Gelieve een compatibele bron voor spoilergegevens te specificeren. - - - Download URL: - Download URL: - - - - Local file: - Restore default URL - Herstel standaard URL - - - - Choose file... - - The spoiler database will be saved at the following location: - De spoiler database wordt op de volgende locatie opgeslagen: - - - - Save to a custom path (not recommended) - Opslaan naar een aangepaste locatie (niet aanbevolen) + The provided URL is not valid. + LoadTokensPage - - Save token database - token database opslaan - - - - XML; token database (*.xml) - XML; token database (*.xml) - - - - tokens + Tokens source selection - - Tokens import - Tokens import + Error + Fout - - Please specify a compatible source for token data. - Gelieve een compatibele bron voor tokengegevens op te geven. + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + - Download URL: - Download URL: - - - - Local file: - Restore default URL - Herstel standaard URL - - - - Choose file... - - The token database will be saved at the following location: - De token database wordt op de volgende locatie opgeslagen: - - - - Save to a custom path (not recommended) - Opslaan naar een aangepaste locatie (niet aanbevolen) + The provided URL is not valid. + OracleImporter - Dummy set containing tokens Token voorbeeldset @@ -284,249 +163,174 @@ OracleWizard - Oracle Importer Oracle importer - - - OutroPage - - Finished - Klaar - - - - The wizard has finished. - De wizard is klaar. - - - - You can now start using Cockatrice with the newly updated cards. - U kunt nu beginnen met het gebruik van Cockatrice met de nieuw bijgewerkte kaarten. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Als de kaartendatabases niet automatisch herladen, start dan de Cockatrice client opnieuw op. + Save + Opslaan SaveSetsPage - - - Error - Fout - - - - No set has been imported. - Er zijn geen sets geïmporteerd. - - - Sets imported Sets geïmporteerd - - A cockatrice database file of %1 MB has been downloaded. - Een cockatrice database bestand van %1 MB is gedownload. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + De volgende sets zijn geïmporteerd. Druk op "Opslaan" om de geïmporteerde kaarten op te slaan in de Cockatrice database. - - The following sets have been found: - De volgende sets zijn gevonden: + Save to the default path (recommended) + Opslaan in standaard pad (aanbevolen) - - Press "Save" to store the imported cards in the Cockatrice database. - Druk op "Opslaan" om de geïmporteerde kaarten op te slaan in de Cockatrice database. + Error + Fout - - The card database will be saved at the following location: - De kaartendatabase wordt op de volgende locatie opgeslagen: + No set has been imported. + Er zijn geen sets geïmporteerd. - - Save to a custom path (not recommended) - Opslaan naar een aangepaste locatie (niet aanbevolen) - - - - &Save - &Opslaan - - - Import finished: %1 cards. Import klaar: %1 kaarten. - %1: %2 cards imported %1: %2 kaarten geïmporteerd - Save card database Kaartendatabase opslaan - XML; card database (*.xml) XML; kaart database (*.xml) - + Success + Succes + + + The card database has been saved successfully to +%1 + De kaartendatabase is succesvol opgeslagen in +%1 + + The file could not be saved to %1 Het bestand kon niet worden opgeslagen in %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error Fout - - The provided URL is not valid: - De ingevoerde URL is niet geldig: - - - - Downloading (0MB) - Downloaden (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Downloaden (%1MB) - - - - Network error: %1. - Netwerk fout: %1. - - - The file could not be saved to %1 - Het bestand kon niet worden opgeslagen naar %1 + UnZip - ZIP operation completed successfully. ZIP taak succesvol voltooid. - Failed to initialize or load zlib library. - Zlib library kon niet geladen of geïnitialiseerd worden. + Zlib bibliotheek kon niet geladen of geïnitialiseerd worden. - zlib library error. - Zlib library fout. + Zlib bibliotheek fout. - Unable to create or open file. Kan het bestand niet openen of aanmaken. - Partially corrupted archive. Some files might be extracted. Gedeeltelijk beschadigd archief. Sommige bestanden kunnen mogelijk uitgepakt worden. - Corrupted archive. Beschadigd archief. - Wrong password. Verkeerd wachtwoord. - No archive has been created yet. Er is nog geen archief gemaakt. - File or directory does not exist. Bestand of map bestaat niet. - File read error. Bestand leesfout. - File write error. Bestand schrijffout. - File seek error. Bestand zoekfout. - Unable to create a directory. Map kon niet aangemaakt worden. - Invalid device. Ongeldig apparaat. - Invalid or incompatible zip archive. - Ongeldig of incompatibel zip archief. + Ongeldig of onverenigbare zip archief. - Inconsistent headers. Archive might be corrupted. Inconsistente headers. Archief is mogelijk beschadigd. - Unknown error. Onbekende fout. @@ -534,75 +338,44 @@ Zip - ZIP operation completed successfully. Zip handeling succesvol voltooid. - Failed to initialize or load zlib library. - Initialisatie van zlib library niet gelukt. + Initialisatie van zlib bibliotheek niet gelukt. - zlib library error. - Zlib library fout. + Zlib bibliotheek fout. - Unable to create or open file. Kon geen bestand openen of aanmaken. - No archive has been created yet. Er is nog geen archief gemaakt. - File or directory does not exist. Bestand of map bestaat niet. - File read error. Bestand leesfout. - File write error. Bestand schrijffout. - File seek error. Bestand zoekfout. - Unknown error. Onbekende fout. - - i18n - - - English - Nederlands (Dutch) - - - - main - - - Only run in spoiler mode - Alleen in spoiler-modus werken - - - - Run in no-confirm background mode - Uitvoeren in achtergrondmodus zonder bevestiging - - \ No newline at end of file diff --git a/oracle/translations/oracle_pl.ts b/oracle/translations/oracle_pl.ts index d5d632f70..f7745b725 100644 --- a/oracle/translations/oracle_pl.ts +++ b/oracle/translations/oracle_pl.ts @@ -1,282 +1,161 @@ - + IntroPage - Introduction Wprowadzenie - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Ten kreator zaimportuje listę dodatków, kart oraz tokenów które zostaną użyte przez Cockatrice. + English + Polski (Polish) - - Interface language: - Język interfejsu: + Language: + Język: - - Version: - Wersja: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + LoadSetsPage - Source selection Wybór źródła - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Proszę podać źródło listy edycji i kart. Można podać adres URL, z którego zostanie pobrana, lub istniejący plik na komputerze. - - - - Download URL: - Pobierz URL - - - Local file: Plik lokalny: - - Restore default URL - Przywróć domyślny URL - - - Choose file... Wybierz plik… - Load sets file Wczytaj listę dodatków - - Sets file (%1) - Sets JSON file (%1) - + Sets JSON file (*.json *.zip) + Plik listy dodatków JSON (*.json, *.zip) + + + Sets JSON file (*.json) + Plik listy dodatków JSON (*.json) - - - - - - - Error Błąd - - The provided URL is not valid. - Podano nieprawidłowy URL. - - - Downloading (0MB) Pobieranie (0MB) - Please choose a file. Proszę wybrać plik. - Cannot open file '%1'. - Nie można otworzyć pliku '%1'. + Nie można otworzyć pliku ‚%1’. - Downloading (%1MB) Pobieranie (%1MB) - Network error: %1. Błąd sieci: %1. - Parsing file Analizowanie pliku - - Xz extraction failed. - Rozpakowanie XZ nie udało się. - - - - Sorry, this version of Oracle does not support xz compressed files. - Przepraszamy, ta wersja Oracle nie obsługuje plików spakowanych w archiwum xz. - - - Failed to open Zip archive: %1. Otwieranie archiwum Zip zakończone niepowodzeniem: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. Rozpakowanie pliku Zip nieudane: archiwum nie zawiera dokładnie jednego pliku. - Zip extraction failed: %1. Rozpakowanie Zip nieudane: %1. - Sorry, this version of Oracle does not support zipped files. Przepraszamy, ta wersja Oracle nie obsługuje plików spakowanych w archiwum Zip. - - Failed to interpret downloaded data. - Interpretacja pobranych danych nie udała się. + Do you want to try to download a fresh copy of the uncompressed file instead? + Czy chcesz zamiast tego pobrać świeżą kopię pliku nieskompresowanego? - - Do you want to download the uncompressed file instead? - Czy chcesz zamiast tego pobrać plik nieskompresowany? - - - The file was retrieved successfully, but it does not contain any sets data. Plik został pobrany z powodzeniem, ale nie zawiera informacji o dodatkach. - - - LoadSpoilersPage - - Save spoiler database - Zapisz bazę danych spoilerów. - - - - XML; spoiler database (*.xml) - XML; baza danych spoilerów (*.xml) - - - - spoiler + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - Spoilers import - Import spoilerów - - - - Please specify a compatible source for spoiler data. - Proszę wybrać kompatybilne źródło danych ze spoilerami. - - - Download URL: - URL pobierania: - - - - Local file: - Restore default URL - Przywróć domyślny URL - - - - Choose file... - - The spoiler database will be saved at the following location: - Baza danych spoilerów zostanie zapisana w tym miejscu: - - - - Save to a custom path (not recommended) - Zapisz do niestandardowej ścieżki (niezalecane) + The provided URL is not valid. + LoadTokensPage - - Save token database - Zapisz bazę danych tokenów. - - - - XML; token database (*.xml) - XML; baza danych tokenów (*.xml) - - - - tokens + Tokens source selection - - Tokens import - Import tokenów + Error + Błąd - - Please specify a compatible source for token data. - Proszę wybrać kompatybilne źródło danych z tokenami. + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + - Download URL: - Pobierz odnośnik: - - - - Local file: - Restore default URL - Przywróć domyślny URL - - - - Choose file... - - The token database will be saved at the following location: - Baza danych tokenów zostanie zapisana w tym miejscu: - - - - Save to a custom path (not recommended) - Zapisz do niestandardowej ścieżki (niezalecane) + The provided URL is not valid. + OracleImporter - Dummy set containing tokens Dodatek-atrapa, zawierający tokeny. @@ -284,249 +163,174 @@ OracleWizard - Oracle Importer Oracle – kreator importu - - - OutroPage - - Finished - Zakończone - - - - The wizard has finished. - Kreator zakończył swoją pracę. - - - - You can now start using Cockatrice with the newly updated cards. - Możesz teraz korzystać z Cockatrice z nowo zaktualizowanymi kartami. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Jeżeli bazy kart nie przeładują się automatycznie, zrestartuj klienta Cockatrice. + Save + Zapisz SaveSetsPage - - - Error - Błąd - - - - No set has been imported. - Nie zaimportowano żadnego dodatku. - - - Sets imported Zaimportowane dodatki - - A cockatrice database file of %1 MB has been downloaded. - Baza danych Cockatrice mająca %1 MB została pobrana. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + Następujące dodatki zostały zaimportowane. Kliknij „Zapisz” by zachować zaimportowane dodatki w bazie danych Cockatrice. - - The following sets have been found: - Następujące dodatki zostały znalezione: + Save to the default path (recommended) + Zapisz do domyślnej ścieżki (zalecane) - - Press "Save" to store the imported cards in the Cockatrice database. - Kliknij "Zapisz" aby zapisać zaimportowane karty w bazie danych Cockatrice. + Error + Błąd - - The card database will be saved at the following location: - Baza danych kart zostanie zapisana w tym miejscu: + No set has been imported. + Nie zaimportowano żadnego dodatku. - - Save to a custom path (not recommended) - Zapisz do niestandardowej ścieżki (niezalecane) - - - - &Save - Zapisz - - - Import finished: %1 cards. Import zakończony: %1 kart. - %1: %2 cards imported %1: zaimportowano %2 kart - Save card database Zapisz bazę kart - XML; card database (*.xml) XML, baza kart (*.xml) - + Success + Powodzenie + + + The card database has been saved successfully to +%1 + Z powodzeniem zapisano bazę kart do +%1 + + The file could not be saved to %1 Plik nie mógł zostać zapisany do %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error Błąd - - The provided URL is not valid: - - - - - Downloading (0MB) - Pobieranie (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Pobieranie (%1MB) - - - - Network error: %1. - Błąd sieci: %1. - - - The file could not be saved to %1 - Plik nie mógł zostać zapisany do %1 + UnZip - ZIP operation completed successfully. Operacja ZIP zakończona powodzeniem. - Failed to initialize or load zlib library. Nieudana inicjalizacja lub załadowanie biblioteki zlib. - zlib library error. Bląd biblioteki zlib. - Unable to create or open file. Nie można utworzyć lub otworzyć pliku. - Partially corrupted archive. Some files might be extracted. Archiwum częściowo uszkodzone. Część plików mogła zostać wypakowana. - Corrupted archive. Archiwum uszkodzone. - Wrong password. - Nieprawidłowe hasło. + Błędne hasło. - No archive has been created yet. Nie utworzono jeszcze archiwum. - File or directory does not exist. Katalog lub plik nie istnieją. - File read error. Błąd odczytu pliku. - File write error. Błąd zapisu pliku. - File seek error. - Błąd wyszukania w czasie odczytu pliku. + Błąd przemieszczenia w czasie odczytu pliku. - Unable to create a directory. - Nie można utworzyć katalogu. + Nieudane utworzenie katalogu. - Invalid device. Nieprawidłowe urządzenie. - Invalid or incompatible zip archive. Nieprawidłowe lub niezgodne archiwum zip. - Inconsistent headers. Archive might be corrupted. Niespójne nagłówki. Archiwum może być uszkodzone. - Unknown error. Nieznany błąd. @@ -534,75 +338,44 @@ Zip - ZIP operation completed successfully. Operacja ZIP zakończona powodzeniem. - Failed to initialize or load zlib library. Nieudana inicjalizacja lub załadowanie biblioteki zlib. - zlib library error. Błąd biblioteki zlib. - Unable to create or open file. Nie można utworzyć lub otworzyć pliku. - No archive has been created yet. Nie utworzono jeszcze archiwum. - File or directory does not exist. Katalog lub plik nie istnieją. - File read error. Błąd odczytu pliku. - File write error. Błąd zapisu pliku. - File seek error. - Błąd wyszukania w czasie odczytu pliku. + Błąd przemieszczenia w czasie odczytu pliku. - Unknown error. Nieznany błąd. - - i18n - - - English - Polski (Polish) - - - - main - - - Only run in spoiler mode - Tylko działaj w trybie spoilerów - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_pt.ts b/oracle/translations/oracle_pt.ts index c578a0365..91118a31e 100644 --- a/oracle/translations/oracle_pt.ts +++ b/oracle/translations/oracle_pt.ts @@ -1,444 +1,264 @@ - + IntroPage - Introduction Introdução - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - + English + Português (Portuguese) - - Interface language: - + Language: + Língua: - - Version: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. LoadSetsPage - Source selection Selecção da fonte - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - URL de download: - - - Local file: Ficheiro local: - - Restore default URL - URL para repor definições de origem - - - Choose file... Escolher ficheiro... - Load sets file Carregar ficheiro das edições - - Sets file (%1) - Sets JSON file (%1) + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) - - - - - - - Error Erro - - The provided URL is not valid. - O URL fornecido não é válido. - - - Downloading (0MB) A efectuar download (0MB) - Please choose a file. Por favor escolha um ficheiro. - Cannot open file '%1'. Impossível abrir ficheiro '%1'. - Downloading (%1MB) A efectuar download (%1MB) - Network error: %1. Erro da rede: %1. - Parsing file - Ficheiro de análise - - - - Xz extraction failed. - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. Abrir archivo zip falhou: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Extracção do ZIP falhada: o arquivo ZIP não contem exactamente um ficheiro. + - Zip extraction failed: %1. Extração do Zip falhada: %1. - Sorry, this version of Oracle does not support zipped files. - Pedimos desculpa, mas esta versão do Oracle não suporta ficheiros zipados. - - - - Failed to interpret downloaded data. - - Do you want to download the uncompressed file instead? + Do you want to try to download a fresh copy of the uncompressed file instead? - The file was retrieved successfully, but it does not contain any sets data. - O ficheiro foi recuperado com sucesso, mas não contem nenhum dado sobre expansões. - - - - LoadSpoilersPage - - - Save spoiler database - - XML; spoiler database (*.xml) + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - - Local file: - - - - Restore default URL - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error + Erro + + + Downloading (0MB) + A efectuar download (0MB) + + + Downloading (%1MB) + A efectuar download (%1MB) + + + Network error: %1. + Erro da rede: %1. + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - - tokens - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - Download URL: - URL de download: - - - - Local file: - Restore default URL - URL para repor definições de origem - - - - Choose file... - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens - Set básico contendo fichas + OracleWizard - Oracle Importer Importar Oracle - - - OutroPage - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - + Save + Guardar SaveSetsPage - - - Error - Erro - - - - No set has been imported. - Nenhuma expansão foi importada. - - - Sets imported Edições importadas - - A cockatrice database file of %1 MB has been downloaded. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - The following sets have been found: + Save to the default path (recommended) - - Press "Save" to store the imported cards in the Cockatrice database. + Error + Erro + + + No set has been imported. - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - Import finished: %1 cards. - Importação terminada: %1 cartas. + - %1: %2 cards imported %1. %2 cartas importadas - Save card database - Guardar base de dados das cartas + - XML; card database (*.xml) - XML; Base de dados de cartas (*.xml) + + + + Success + Sucedido + + + The card database has been saved successfully to +%1 + - The file could not be saved to %1 - O ficheiro não pode ser gravado em %1 + - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + Sucesso + + + The token database has been saved successfully to +%1 - - - - - Error - + Erro - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 @@ -446,87 +266,70 @@ UnZip - ZIP operation completed successfully. - Operação ZIP completa com sucesso. + - Failed to initialize or load zlib library. - Falha na inicialização ou carregamento da biblioteca zlib + - zlib library error. - Erro da biblioteca zlib + - Unable to create or open file. - Incapaz de criar ou abrir ficheiro + - Partially corrupted archive. Some files might be extracted. - Arquivo parcialmente corrompido. Alguns ficheiros possivelmente serão extraídos. + - Corrupted archive. Archivo corrompido. - Wrong password. Senha incorrecta. - No archive has been created yet. Ainda não foi criado nenhum arquivo - File or directory does not exist. - O ficheiro ou directório não existe. + - File read error. - Erro na leitura do ficheiro. + - File write error. - erro na escrita do ficheiro. + - File seek error. - Erro na procura do ficheiro. + - Unable to create a directory. - Incapaz de criar directório. + - Invalid device. - Dispositivo inválido. + - Invalid or incompatible zip archive. - Arquivo ZIP inválido ou incompatível + - Inconsistent headers. Archive might be corrupted. - Cabeçalhos inconsistentes. O arquivo pode estar corrompido, + - Unknown error. Erro desconhecido. @@ -534,75 +337,44 @@ Zip - ZIP operation completed successfully. - Operação ZIP completa com sucesso. + - Failed to initialize or load zlib library. - Falha na inicialização ou carregamento da biblioteca zlib + - zlib library error. - Erro da biblioteca zlib + - Unable to create or open file. - Incapaz de criar ou abrir ficheiro + - No archive has been created yet. Ainda não foi criado nenhum arquivo - File or directory does not exist. - O ficheiro ou directório não existe. + - File read error. - Erro na leitura do ficheiro. + - File write error. - erro na escrita do ficheiro. + - File seek error. - Erro na procura do ficheiro. + - Unknown error. Erro desconhecido. - - i18n - - - English - Português (Portuguese) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_pt_BR.ts b/oracle/translations/oracle_pt_BR.ts index 96a6382ec..10516f0f8 100644 --- a/oracle/translations/oracle_pt_BR.ts +++ b/oracle/translations/oracle_pt_BR.ts @@ -1,282 +1,161 @@ - + IntroPage - Introduction Introdução - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Esse recurso irá importar a lista de coleções, cartas e fichas que serão utilizadas pelo Cokatrice. + English + Português do Brasil (Brazilian Portuguese) - - Interface language: - Idioma da interface: + Language: + Língua: - - Version: - Versão: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + LoadSetsPage - Source selection Selecione a origem - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Por favor especifique uma fonte compatível de expansões e cartas. Você pode especificar um endereço URL que será baixado ou usar um arquivo existente no seu computador. - - - - Download URL: - Endereço para download: - - - Local file: Arquivo local: - - Restore default URL - Restaurar URL padrão - - - Choose file... - Escolha o arquivo... + Escolher arquivo... - Load sets file Carregar arquivos de expansão - - Sets file (%1) - Sets JSON file (%1) - Arquivo de expansões (%1) + Sets JSON file (*.json *.zip) + Arquivo JSON de expansão (*.json *.zip) + + + Sets JSON file (*.json) + Arquivo JSON de expansão (*.json) - - - - - - - Error Erro - - The provided URL is not valid. - A URL escolhida não é valida - - - Downloading (0MB) Baixando (0MB) - Please choose a file. Por favor, escolha um arquivo; - Cannot open file '%1'. Não pode abrir arquivo '%1' - Downloading (%1MB) Baixando (%1MB) - Network error: %1. Erro na conexão com a internet: %1. - Parsing file Analisando arquivo - - Xz extraction failed. - Extração de arquivo .xz falhou - - - - Sorry, this version of Oracle does not support xz compressed files. - Desculpe, esta versão do Oracle não suporta arquivos comprimidos no formato .xz - - - Failed to open Zip archive: %1. Falhou em abrir o arquivo Zip: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. A extração do arquivo ZIP falhou: o arquivo ZIP não contém exatamente um arquivo. - Zip extraction failed: %1. Extração do arquivo Zip falhou: %1. - Sorry, this version of Oracle does not support zipped files. Desculpe, esta versão do Oracle não suporta arquivos compactados. - - Failed to interpret downloaded data. - Falha ao interpretar dados baixados + Do you want to try to download a fresh copy of the uncompressed file instead? + Você quer tentar baixar uma nova cópia do arquivo não comprimido? - - Do you want to download the uncompressed file instead? - Gostaria de baixar os arquivos descomprimidos, em vez disso? - - - The file was retrieved successfully, but it does not contain any sets data. O arquivo foi recebido com sucesso, mas não contém qualquer informação de expansão. - - - LoadSpoilersPage - - Save spoiler database - Salvar base de dados de spoiler - - - - XML; spoiler database (*.xml) - XML; base de dados de spoilers (*.xml) - - - - spoiler + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - Spoilers import - Importar spoilers - - - - Please specify a compatible source for spoiler data. - Por favor, selecione uma fonte compatível para os dados de spoilers. - - - Download URL: - Endereço para download: - - - - Local file: - Restore default URL - Restaurar URL padrão - - - - Choose file... - - The spoiler database will be saved at the following location: - A base de dados de spoilers será salva no seguinte diretório: - - - - Save to a custom path (not recommended) - Salvar em diretório customizado (não recomendado) + The provided URL is not valid. + LoadTokensPage - - Save token database - Salvar base de dados de tokens - - - - XML; token database (*.xml) - XML; base de dados de token (*.xml) - - - - tokens + Tokens source selection - - Tokens import - Importar tokens + Error + - - Please specify a compatible source for token data. - Por favor especifique uma fonte compatível para os dados de tokens. + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + - Download URL: - Endereço para download: - - - - Local file: - Restore default URL - Restaurar URL padrão - - - - Choose file... - - The token database will be saved at the following location: - A base de dados de tokens será salva no seguinte diretório: - - - - Save to a custom path (not recommended) - Salvar em diretório customizado (não recomendado) + The provided URL is not valid. + OracleImporter - Dummy set containing tokens Esta expansão contém fichas. @@ -284,249 +163,174 @@ OracleWizard - Oracle Importer Importador Oracle - - - OutroPage - - Finished - Finalizado - - - - The wizard has finished. - O recurso terminou. - - - - You can now start using Cockatrice with the newly updated cards. - Agora você pode começar a usar o Cockatrice com as novas cartas atualizadas. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - Se a base de dados das cartas não for atualizada automaticamente, reinicie o cliente do Cockatrice. + Save + Salvar SaveSetsPage - - - Error - Erro - - - - No set has been imported. - O set não foi importado. - - - Sets imported Expansões importadas - - A cockatrice database file of %1 MB has been downloaded. - Um arquivo de %1 MB de banco de dados do cockatrice foi baixado. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + As seguintes expansões foram importadas. Pressione "Salvar" para salvar as cartas importadas ao banco de dados do Cockatrice. - - The following sets have been found: - As seguintes expansões foram encontradas: + Save to the default path (recommended) + Salve para o caminho padrão (recomendado) - - Press "Save" to store the imported cards in the Cockatrice database. - Aperte "Salvar" para guardar as cartas importadas na base de dados do Cockatrice. + Error + Erro - - The card database will be saved at the following location: - A base de dados de cartas será salva no seguinte diretório: + No set has been imported. + O set não foi importado. - - Save to a custom path (not recommended) - Salvar em diretório customizado (não recomendado) - - - - &Save - &Salvar - - - Import finished: %1 cards. Importação finalizada: %1 cartas. - %1: %2 cards imported %1: %2 cartas importadas - Save card database Carta salva no banco de dados - XML; card database (*.xml) XML; banco de dados de cartas (*.xml) - + Success + Sucesso + + + The card database has been saved successfully to +%1 + O banco de dados das cartas foram salvas com sucesso para +%1 + + The file could not be saved to %1 O arquivo não pode ser salvo para %1 - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error - Erro - - - - The provided URL is not valid: - - Downloading (0MB) - Baixando (0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - Baixando (%1MB) - - - - Network error: %1. - Erro de rede: %1 - - - The file could not be saved to %1 - O arquivo não pode ser salvo em %1 + UnZip - ZIP operation completed successfully. - Operação de compactação concluída. + Operação ZIP completa com sucesso - Failed to initialize or load zlib library. - Falha ao inicializar ou carregar a biblioteca zlib. + Falha em inicializar ou carregar a biblioteca zlib - zlib library error. - Erro na biblioteca zlib. + Erro na biblioteca zlib - Unable to create or open file. - Não foi possível criar ou abrir o arquivo. + Incapaz de criar ou abrir um arquivo - Partially corrupted archive. Some files might be extracted. - Arquivo parcialmente corrompido. Alguns arquivos podem ter sido extraídos. + Arquivo parcialmente corrompido. Alguns arquivos puderam ser extraídos. - Corrupted archive. Arquivo corrompido. - Wrong password. Senha incorreta. - No archive has been created yet. - Nenhum arquivo foi criado ainda. + Nenhum arquivo foi criado anteriormente. - File or directory does not exist. Arquivo ou diretório não existe. - File read error. - Erro de leitura do arquivo. + Erro na leitura do arquivo. - File write error. - Erro de gravação do arquivo. + Erro na escrita do arquivo. - File seek error. - Erro ao buscar no arquivo + Erro na busca do arquivo. - Unable to create a directory. - Não foi possível criar um diretório. + Incapaz de criar um diretório. - Invalid device. - Dispositivo inválido. + Aparelho inválido. - Invalid or incompatible zip archive. - Arquivo ZIP inválido ou incompatível. + Arquivo zip inválido ou incompatível. - Inconsistent headers. Archive might be corrupted. - Cabeçalhos inconsistentes. O arquivo pode estar corrompido. + Cabeçalho inconsistente. Arquivo pode estar corrompido. - Unknown error. Erro desconhecido. @@ -534,75 +338,44 @@ Zip - ZIP operation completed successfully. - Operação de compactação concluída com sucesso. + Operação ZIP completa com sucesso. - Failed to initialize or load zlib library. - Falha ao inicializar ou carregar a biblioteca zlib. + Falha em inicializar ou carregar biblioteca zlib. - zlib library error. - Erro na biblioteca zlib. + Erro biblioteca zlib. - Unable to create or open file. - Não foi possível criar ou abrir o arquivo. + Incapaz de criar ou abrir arquivo. - No archive has been created yet. - Nenhum arquivo foi criado ainda. + Nenhum arquivo foi criado anteriormente. - File or directory does not exist. Arquivo ou diretório não existe. - File read error. - Erro de leitura do arquivo. + Erro na leitura do arquivo. - File write error. - Erro de gravação do arquivo. + Erro na escrita do arquivo. - File seek error. - Erro ao buscar no arquivo + Erro na busca do arquivo. - Unknown error. Erro desconhecido. - - i18n - - - English - Português do Brasil (Brazilian Portuguese) - - - - main - - - Only run in spoiler mode - Rodar apenas em modo de 'spoiler' - - - - Run in no-confirm background mode - - - \ No newline at end of file diff --git a/oracle/translations/oracle_ru.ts b/oracle/translations/oracle_ru.ts index e2ff5b5a0..a3e32d78f 100644 --- a/oracle/translations/oracle_ru.ts +++ b/oracle/translations/oracle_ru.ts @@ -1,444 +1,264 @@ - + IntroPage - Introduction - Введение + - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Эта программа импортирует перечень выпусков, карт и фишек, которые будут использоваться в Cockatrice. + English + Русский (Russian) - - Interface language: - Язык интерфейса: + Language: + - - Version: - Версия + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + LoadSetsPage - Source selection - Выбор источника + - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Пожалуйста, укажите источник для перечня сетов и карт. Вы можете использовать ссылку или уже существующий локальный файл. - - - - Download URL: - Ссылка на скачивание: - - - Local file: - Локальный файл: + - - Restore default URL - Восстановить ссылку по умолчанию - - - Choose file... - Выбрать файл.. + - Load sets file - Загрузить файл сетов + - - Sets file (%1) - Sets JSON file (%1) - Файл (%1) + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + - - - - - - - Error - Ошибка + - - The provided URL is not valid. - Предоставленная ссылка некорректна. - - - Downloading (0MB) - Загрузка (0MB) + - Please choose a file. - Пожалуйста, выберите файл. + - Cannot open file '%1'. - Не удалось открыть файл '%1'. + - Downloading (%1MB) - Загрузка (%1MB) + - Network error: %1. - Ошибка сети: %1. + - Parsing file - Ожидание файла + - - Xz extraction failed. - Ошибка распаковки архива - - - - Sorry, this version of Oracle does not support xz compressed files. - Данная версия Oracle не поддерживает архивы xz - - - Failed to open Zip archive: %1. - Не удалось загрузить zip-архив: %1. + - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Ошибка распаковки: zip-архив содержит больше одного файла. + - Zip extraction failed: %1. - Ошибка распаковки zip-архива: %1. + - Sorry, this version of Oracle does not support zipped files. - Данная версия Oracle не поддерживает zip-архивы. + - - Failed to interpret downloaded data. - Ошибка в обработке загруженных данных + Do you want to try to download a fresh copy of the uncompressed file instead? + - - Do you want to download the uncompressed file instead? - Хотите вместо этого загрузить новую несжатую копию? - - - The file was retrieved successfully, but it does not contain any sets data. - Файл успешно получен, но в нем не содержится данных о сетах. - - - - LoadSpoilersPage - - - Save spoiler database - Сохранить базу карт - - - - XML; spoiler database (*.xml) - XML; база карт (*.xml) - - - - spoiler - - Spoilers import - Импорт карт + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + - - Please specify a compatible source for spoiler data. - Укажите совместимый ресурс для импорта базы карт - - - Download URL: - Ссылка на скачивание: - - - - Local file: - Restore default URL - Восстановить ссылку по умолчанию - - - - Choose file... - - The spoiler database will be saved at the following location: - База карт будет сохранена в следующей директории: - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error - - tokens + Downloading (0MB) - - Tokens import + Downloading (%1MB) - - Please specify a compatible source for token data. + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - Download URL: - Ссылка на скачивание: - - - - Local file: - Restore default URL - Восстановить ссылку по умолчанию - - - - Choose file... - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens - Пример сета с фишками + OracleWizard - Oracle Importer - Импортер Oracle - - - - OutroPage - - - Finished - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. + Save SaveSetsPage - - - Error - Ошибка - - - - No set has been imported. - Не было импортировано ни одного сета. - - - Sets imported - Импортировано сетов - - - - A cockatrice database file of %1 MB has been downloaded. - - The following sets have been found: + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - Press "Save" to store the imported cards in the Cockatrice database. + Save to the default path (recommended) - - The card database will be saved at the following location: + Error - - Save to a custom path (not recommended) + No set has been imported. - - &Save - Сохранить - - - Import finished: %1 cards. - Импорт завершен: %1 карт. + - %1: %2 cards imported - %1: %2 карт импортировано + - Save card database - Сохранить базу карт + - XML; card database (*.xml) - База карт (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + - The file could not be saved to %1 - Не удалось сохранить файл в %1 + - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - The file could not be saved to %1 @@ -446,162 +266,114 @@ UnZip - ZIP operation completed successfully. - Распаковка успешно завершена. + - Failed to initialize or load zlib library. - Не удалось загрузить библиотеку .zlib. + - zlib library error. - ошибка библиотеки .zlib + - Unable to create or open file. - Не удалось создать или открыть файл. + - Partially corrupted archive. Some files might be extracted. - Архив частично поврежден. Некоторые файлы могут быть извлечены. + - Corrupted archive. - Архив поврежден. + - Wrong password. - Неверный пароль. + - No archive has been created yet. - Не было создано ни одного архива. + - File or directory does not exist. - Файл или директория не существуют. + - File read error. - Ошибка чтения файла. + - File write error. - Ошибка записи файла. + - File seek error. - Не удалось найти файл. + - Unable to create a directory. - Не удалось создать директорию. + - Invalid device. - Неверное устройство. + - Invalid or incompatible zip archive. - Неверный или несовместимый zip-архив. + - Inconsistent headers. Archive might be corrupted. - Несовместимые заголовки. Архив может быть поврежден. + - Unknown error. - Неизвестная ошибка. + Zip - ZIP operation completed successfully. - Распаковка успешно завершена. + - Failed to initialize or load zlib library. - Не удалось загрузить библиотеку .zlib. + - zlib library error. - ошибка библиотеки .zlib + - Unable to create or open file. - Не удалось создать или открыть файл. + - No archive has been created yet. - Не было создано ни одного архива. + - File or directory does not exist. - Файл или директория не существуют. + - File read error. - Ошибка чтения файла. + - File write error. - Ошибка записи файла. + - File seek error. - Не удалось найти файл. + - Unknown error. - Неизвестная ошибка. - - - - i18n - - - English - Русский (Russian) - - - - main - - - Only run in spoiler mode - Запускать только в режиме сетов - - - - Run in no-confirm background mode diff --git a/oracle/translations/oracle_sr.ts b/oracle/translations/oracle_sr.ts deleted file mode 100644 index 04d48688a..000000000 --- a/oracle/translations/oracle_sr.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - Uvod - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - - - - - Interface language: - - - - - Version: - - - - - LoadSetsPage - - - Source selection - Odabir izvora - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - URL za preuzimanje: - - - - Local file: - Lokalni fajl: - - - - Restore default URL - Povrati uobičajeni URL - - - - Choose file... - Izaberite fajl... - - - - Load sets file - - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - Greška - - - - The provided URL is not valid. - Dati URL nije važeći. - - - - Downloading (0MB) - Preuzimanje (0MB) - - - - Please choose a file. - Molimo Vas izaberite fajl. - - - - Cannot open file '%1'. - Nemoguće otvori fajl '%1'. - - - - Downloading (%1MB) - Preuzimanje (%1MB) - - - - Network error: %1. - Mrežna greška: %1. - - - - Parsing file - Parsiranje fajla - - - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - - Failed to open Zip archive: %1. - Neuspeh u otvaranju Zip arhive: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - - - - - Zip extraction failed: %1. - - - - - Sorry, this version of Oracle does not support zipped files. - Izvinite, ova verzija Oracle-a ne podržava zip fajlove. - - - - Failed to interpret downloaded data. - - - - - Do you want to download the uncompressed file instead? - - - - - The file was retrieved successfully, but it does not contain any sets data. - - - - - LoadSpoilersPage - - - Save spoiler database - - - - - XML; spoiler database (*.xml) - - - - - spoiler - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - LoadTokensPage - - - Save token database - - - - - XML; token database (*.xml) - - - - - tokens - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - - Download URL: - URL za preuzimanje: - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - OracleImporter - - - Dummy set containing tokens - - - - - OracleWizard - - - Oracle Importer - - - - - OutroPage - - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - - - - - SaveSetsPage - - - - Error - Greška - - - - No set has been imported. - - - - - Sets imported - - - - - A cockatrice database file of %1 MB has been downloaded. - - - - - The following sets have been found: - - - - - Press "Save" to store the imported cards in the Cockatrice database. - - - - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - - - - - %1: %2 cards imported - - - - - Save card database - - - - - XML; card database (*.xml) - - - - - The file could not be saved to %1 - - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - - - - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - The file could not be saved to %1 - - - - - UnZip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - - - - - Partially corrupted archive. Some files might be extracted. - - - - - Corrupted archive. - - - - - Wrong password. - Pogrešna lozinka. - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - Greška u čitanju fajla. - - - - File write error. - Greška u pisanju fajla. - - - - File seek error. - Greška u traženju fajla. - - - - Unable to create a directory. - - - - - Invalid device. - - - - - Invalid or incompatible zip archive. - - - - - Inconsistent headers. Archive might be corrupted. - - - - - Unknown error. - Nepoznata greška. - - - - Zip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - Nemoguće napraviti ili otvoriti fajl. - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - Greška u čitanju fajla. - - - - File write error. - Greška u pisanju fajla. - - - - File seek error. - Greška u traženju fajla. - - - - Unknown error. - Nepoznata greška. - - - - i18n - - - English - Srpski (Serbian) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_sv.ts b/oracle/translations/oracle_sv.ts index 89979872d..432978fe1 100644 --- a/oracle/translations/oracle_sv.ts +++ b/oracle/translations/oracle_sv.ts @@ -1,245 +1,161 @@ - + IntroPage - Introduction - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. + English + Svenska (Swedish) + + + Language: - - Interface language: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. - - - Version: - Version: - LoadSetsPage - Source selection - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - - - - Download URL: - - - - Local file: - - Restore default URL - - - - Choose file... - Load sets file - - Sets JSON file (%1) + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) - - - - - - Error - - The provided URL is not valid. - - - - Downloading (0MB) - Please choose a file. - Cannot open file '%1'. - Downloading (%1MB) - Network error: %1. - Parsing file - - Xz extraction failed. - - - - - Sorry, this version of Oracle does not support xz compressed files. - - - - Failed to open Zip archive: %1. - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip extraction failed: %1. - Sorry, this version of Oracle does not support zipped files. - - Do you want to download the uncompressed file instead? + Do you want to try to download a fresh copy of the uncompressed file instead? - The file was retrieved successfully, but it does not contain any sets data. - - - LoadSpoilersPage - - Save spoiler database + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - - XML; spoiler database (*.xml) - - - - - Spoilers import - - - - - Please specify a compatible source for spoiler data. - - - - Download URL: - Restore default URL - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. LoadTokensPage - - Save token database + Tokens source selection - - XML; token database (*.xml) + Error - - Tokens import + Downloading (0MB) - - Please specify a compatible source for token data. + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. - Download URL: - Restore default URL - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) + The provided URL is not valid. OracleImporter - Dummy set containing tokens @@ -247,151 +163,217 @@ OracleWizard - Oracle Importer - - - OutroPage - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. + Save SaveSetsPage - - - Error - - - - - No set has been imported. - - - - Sets imported - - The following sets have been found: + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. - - Press "Save" to store the imported cards in the Cockatrice database. + Save to the default path (recommended) - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - - - - - %1: %2 cards imported - - - - - Save card database - - - - - XML; card database (*.xml) - - - - - The file could not be saved to %1 - - - - - SimpleDownloadFilePage - - - - Error - - The provided URL is not valid. + No set has been imported. - - Downloading (0MB) + Import finished: %1 cards. - - Downloading (%1MB) + %1: %2 cards imported - - Network error: %1. + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 - The file could not be saved to %1 - i18n + SaveTokensPage - - English - Svenska (Swedish) + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + - main + UnZip - - Only run in spoiler mode + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. diff --git a/oracle/translations/oracle_tr.ts b/oracle/translations/oracle_tr.ts deleted file mode 100644 index 517f9248e..000000000 --- a/oracle/translations/oracle_tr.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - Açıklama - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - Bu sihirbaz Cockatrice tarafından kullanılacak setlerin, kartların ve tokenlerin listesini içe aktaracaktır. - - - - Interface language: - Arayüz dili: - - - - Version: - Sürüm: - - - - LoadSetsPage - - - Source selection - Kaynak seçimi - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - Lütfen set ve kart listesi için uyumlu bir kaynak belirtin. İndirilecek bir URL adresi belirtebilir veya bilgisayarınızdaki mevcut bir dosyayı kullanabilirsiniz. - - - - Download URL: - İndirme Bağlantısı: - - - - Local file: - Yerel dosya: - - - - Restore default URL - Varsayılan URL'yi geri yükle - - - - Choose file... - Dosya seç... - - - - Load sets file - Set dosyasını yükle - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - Hata - - - - The provided URL is not valid. - Paylaşılan bağlantı geçerli değil. - - - - Downloading (0MB) - İndiriliyor (0MB) - - - - Please choose a file. - Lütfen dosya seçin. - - - - Cannot open file '%1'. - Dosya açılamıyor '1%'. - - - - Downloading (%1MB) - İndiriliyor (%1MB) - - - - Network error: %1. - Bağlantı hatası: %1 - - - - Parsing file - Dosya ayrıştırılıyor - - - - Xz extraction failed. - Xz çıkarma işlemi başarısız oldu. - - - - Sorry, this version of Oracle does not support xz compressed files. - Maalesef, Oracle'ın bu sürümü xz sıkıştırılmış dosyaları desteklemiyor. - - - - Failed to open Zip archive: %1. - Zip dosyası açılamadı: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - Zip çıkarma işlemi başarısız oldu: Zip arşivi tam olarak bir dosya içermiyor. - - - - Zip extraction failed: %1. - Zip çıkarma işlemi başarısız oldu: %1. - - - - Sorry, this version of Oracle does not support zipped files. - Maalesef, Oracle'ın bu sürümü zip dosyalarını desteklemiyor. - - - - Failed to interpret downloaded data. - İndirilen veriler işlenemedi. - - - - Do you want to download the uncompressed file instead? - Bunun yerine sıkıştırılmamış dosyayı indirmek ister misiniz? - - - - The file was retrieved successfully, but it does not contain any sets data. - Dosya başarıyla alındı, ancak herhangi bir set verisi içermiyor. - - - - LoadSpoilersPage - - - Save spoiler database - Spoiler veritabanını kaydet - - - - XML; spoiler database (*.xml) - XML; spoiler veritabanı (*.xml) - - - - spoiler - - - - - Spoilers import - Spoilers içe aktar - - - - Please specify a compatible source for spoiler data. - Lütfen spoiler verileri için uyumlu bir kaynak belirtin. - - - - Download URL: - İndirme Bağlantısı: - - - - Local file: - - - - - Restore default URL - Varsayılan bağlantıyı geri yükle - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - LoadTokensPage - - - Save token database - - - - - XML; token database (*.xml) - - - - - tokens - - - - - Tokens import - - - - - Please specify a compatible source for token data. - - - - - Download URL: - - - - - Local file: - - - - - Restore default URL - - - - - Choose file... - - - - - The token database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - OracleImporter - - - Dummy set containing tokens - - - - - OracleWizard - - - Oracle Importer - - - - - OutroPage - - - Finished - - - - - The wizard has finished. - - - - - You can now start using Cockatrice with the newly updated cards. - - - - - If the card databases don't reload automatically, restart the Cockatrice client. - - - - - SaveSetsPage - - - - Error - - - - - No set has been imported. - - - - - Sets imported - - - - - A cockatrice database file of %1 MB has been downloaded. - - - - - The following sets have been found: - - - - - Press "Save" to store the imported cards in the Cockatrice database. - - - - - The card database will be saved at the following location: - - - - - Save to a custom path (not recommended) - - - - - &Save - - - - - Import finished: %1 cards. - - - - - %1: %2 cards imported - - - - - Save card database - - - - - XML; card database (*.xml) - - - - - The file could not be saved to %1 - - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - - - - - The provided URL is not valid: - - - - - Downloading (0MB) - - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - - - - - Network error: %1. - - - - - The file could not be saved to %1 - - - - - UnZip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - - - - - Partially corrupted archive. Some files might be extracted. - - - - - Corrupted archive. - - - - - Wrong password. - - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - - - - - File write error. - - - - - File seek error. - - - - - Unable to create a directory. - - - - - Invalid device. - - - - - Invalid or incompatible zip archive. - - - - - Inconsistent headers. Archive might be corrupted. - - - - - Unknown error. - - - - - Zip - - - ZIP operation completed successfully. - - - - - Failed to initialize or load zlib library. - - - - - zlib library error. - - - - - Unable to create or open file. - - - - - No archive has been created yet. - - - - - File or directory does not exist. - - - - - File read error. - - - - - File write error. - - - - - File seek error. - - - - - Unknown error. - - - - - i18n - - - English - Türkçe (Turkish) - - - - main - - - Only run in spoiler mode - - - - - Run in no-confirm background mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_uk.ts b/oracle/translations/oracle_uk.ts new file mode 100644 index 000000000..ac24e2fe4 --- /dev/null +++ b/oracle/translations/oracle_uk.ts @@ -0,0 +1,380 @@ + + + IntroPage + + Introduction + + + + English + + + + Language: + + + + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + + + + + LoadSetsPage + + Source selection + + + + Local file: + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + LoadTokensPage + + Tokens source selection + + + + Error + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + + + + \ No newline at end of file diff --git a/oracle/translations/oracle_yue.ts b/oracle/translations/oracle_yue.ts deleted file mode 100644 index 13a621b9e..000000000 --- a/oracle/translations/oracle_yue.ts +++ /dev/null @@ -1,608 +0,0 @@ - - - IntroPage - - - Introduction - 介绍 - - - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - 此嚮導將會導入Cockatrice中用到的系列,卡牌和衍生物列表。 - - - - Interface language: - 介面語言: - - - - Version: - 版本: - - - - LoadSetsPage - - - Source selection - 選擇來源 - - - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - 請選擇合適的牌組和卡片清單來源。您可以輸入下載URL或使用電腦中已有的檔案。 - - - - Download URL: - 下载URL: - - - - Local file: - 本地檔案: - - - - Restore default URL - 恢復常用URL - - - - Choose file... - 選擇檔案... - - - - Load sets file - 载入牌组檔案 - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - - - - - Error - 出現錯誤 - - - - The provided URL is not valid. - 所提供的url無效. - - - - Downloading (0MB) - 下載中 (0MB) - - - - Please choose a file. - 請選擇檔案 - - - - Cannot open file '%1'. - 不能開啟檔案 '%1 - - - - Downloading (%1MB) - 下載中 (%1MB) - - - - Network error: %1. - 網路出錯: %1. - - - - Parsing file - 分析檔案中 - - - - Xz extraction failed. - xz解壓縮失敗. - - - - Sorry, this version of Oracle does not support xz compressed files. - 很抱歉,現前Oracle版本不支援xc壓縮檔案 - - - - Failed to open Zip archive: %1. - 未能開啟壓縮文件: %1. - - - - Zip extraction failed: the Zip archive doesn't contain exactly one file. - 解壓縮失敗:這壓縮文件擁有超過一個檔案. - - - - Zip extraction failed: %1. - 解壓縮失敗: %1. - - - - Sorry, this version of Oracle does not support zipped files. - 很抱歉,現前Oracle版本不支援壓縮檔案. - - - - Failed to interpret downloaded data. - 不能分析下載資料. - - - - Do you want to download the uncompressed file instead? - 你是否想另再下載未經壓縮的檔案? - - - - The file was retrieved successfully, but it does not contain any sets data. - 雖然檔案成功取回, 但是檔案並未含有任何牌組資料. - - - - LoadSpoilersPage - - - Save spoiler database - 儲存預覽資料庫 - - - - XML; spoiler database (*.xml) - XML ; 預覽資料庫(*.XML) - - - - spoiler - - - - - Spoilers import - 導入預覽卡牌 - - - - Please specify a compatible source for spoiler data. - 請指定預覽資料合用來源. - - - - Download URL: - 下载URL: - - - - Local file: - - - - - Restore default URL - 恢復常用URL - - - - Choose file... - - - - - The spoiler database will be saved at the following location: - 預覽資料庫將會儲藏於以下位置 - - - - Save to a custom path (not recommended) - 儲存到自訂路徑(不建議) - - - - LoadTokensPage - - - Save token database - 儲存衍生物資料庫 - - - - XML; token database (*.xml) - XML; 衍生物資料庫 (*.xml) - - - - tokens - - - - - Tokens import - 導入衍生物 - - - - Please specify a compatible source for token data. - 請指定衍生物資料合用來源. - - - - Download URL: - 下载URL: - - - - Local file: - - - - - Restore default URL - 恢復常用URL - - - - Choose file... - - - - - The token database will be saved at the following location: - 衍生物資料庫將會儲藏於以下位置: - - - - Save to a custom path (not recommended) - 儲存到自訂路徑(不建議) - - - - OracleImporter - - - Dummy set containing tokens - 包含衍生物的虚拟牌組 - - - - OracleWizard - - - Oracle Importer - Oracle導入器 - - - - OutroPage - - - Finished - 完成 - - - - The wizard has finished. - 嚮導程序工作完成 - - - - You can now start using Cockatrice with the newly updated cards. - Cockatrice已載入更新了卡牌, 現在已經可以使用. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - 如果卡牌資料庫尚未能自動更新,請把cockatrice用戶端重新開啟. - - - - SaveSetsPage - - - - Error - 出現錯誤 - - - - No set has been imported. - 沒有牌組被導入。 - - - - Sets imported - 已導入牌組 - - - - A cockatrice database file of %1 MB has been downloaded. - 有%1MB大小的Cockatrice資料庫檔案已被下載 - - - - The following sets have been found: - 已找到以下牌组: - - - - Press "Save" to store the imported cards in the Cockatrice database. - 要將導入卡牌保存於Cockatrice資料庫內請按"儲藏". - - - - The card database will be saved at the following location: - 卡牌資料庫將被儲藏於以下位置 - - - - Save to a custom path (not recommended) - 儲存到自訂路徑(不建議) - - - - &Save - &儲存 - - - - Import finished: %1 cards. - 導入過程完成:成功導入%1張卡牌. - - - - %1: %2 cards imported - %1:成功導入%2張卡牌. - - - - Save card database - 儲存卡牌資料庫 - - - - XML; card database (*.xml) - XML; 卡牌資料庫 (*.xml) - - - - The file could not be saved to %1 - 檔案未能儲藏於%1。 - - - - SimpleDownloadFilePage - - - Load %1 file - - - - - %1 file (%1) - - - - - - - - - Error - 出現錯誤 - - - - The provided URL is not valid: - - - - - Downloading (0MB) - 下載中(0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - 下載中 (%1MB) - - - - Network error: %1. - 網路出錯: %1. - - - - The file could not be saved to %1 - 檔案未能儲藏於%1 - - - - UnZip - - - ZIP operation completed successfully. - 檔案壓縮成功 - - - - Failed to initialize or load zlib library. - zlib函式庫初始化或導入失敗 - - - - zlib library error. - zlib函式庫出現錯誤 - - - - Unable to create or open file. - 無法創建或開啟檔案。 - - - - Partially corrupted archive. Some files might be extracted. - 本存檔已受到局部損壞,部分被壓縮檔案可能已被解壓. - - - - Corrupted archive. - 存檔已受損壞. - - - - Wrong password. - 密碼錯誤. - - - - No archive has been created yet. - 存檔尚未創建. - - - - File or directory does not exist. - 檔案或目錄並不存在. - - - - File read error. - 檔案讀取出錯 - - - - File write error. - 檔案寫入出錯 - - - - File seek error. - 檔案尋找出錯 - - - - Unable to create a directory. - 無法創建目錄 - - - - Invalid device. - 裝置無效. - - - - Invalid or incompatible zip archive. - 無效或不配合的壓縮存檔. - - - - Inconsistent headers. Archive might be corrupted. - 本檔案標題並不一致.本存檔可能以受損壞. - - - - Unknown error. - 不知名錯誤 - - - - Zip - - - ZIP operation completed successfully. - 檔案壓縮成功 - - - - Failed to initialize or load zlib library. - zlib函式庫初始化或導入失敗 - - - - zlib library error. - zlib函式庫出現錯誤 - - - - Unable to create or open file. - 無法創建或開啟檔案。 - - - - No archive has been created yet. - 存檔尚未創建. - - - - File or directory does not exist. - 檔案或目錄並不存在. - - - - File read error. - 檔案讀取出錯. - - - - File write error. - 檔案寫入出錯. - - - - File seek error. - 檔案尋找出錯. - - - - Unknown error. - 不知名錯誤. - - - - i18n - - - English - - - - - main - - - Only run in spoiler mode - 限於在預覽模式運行 - - - - Run in no-confirm background mode - - - - \ No newline at end of file diff --git a/oracle/translations/oracle_zh-Hans.ts b/oracle/translations/oracle_zh-Hans.ts index ff19ab463..e7eb2103e 100644 --- a/oracle/translations/oracle_zh-Hans.ts +++ b/oracle/translations/oracle_zh-Hans.ts @@ -1,607 +1,379 @@ - + IntroPage - Introduction - 介绍 + - - This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. - 此向导将会导入Cockatrice中用到的系列,卡牌和衍生物列表. + English + 简体中文 (Chinese Simplified) - - Interface language: - 界面语言: + Language: + - - Version: - 版本: + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + LoadSetsPage - Source selection - 选择来源 + - - Please specify a compatible source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. - 请选择合适的牌组和卡片清单来源。您可以输入下载URL或使用电脑中已有的档案。 - - - - Download URL: - 下载URL: - - - Local file: - 本地文件: + - - Restore default URL - 恢复常用URL - - - Choose file... - 选择档案... + - Load sets file - 载入牌组档案 - - - - Sets file (%1) - Sets JSON file (%1) - - - - - - - + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + Error - 出现错误 + - - The provided URL is not valid. - 所提供的url无效. - - - Downloading (0MB) - 下载中(0MB) + - Please choose a file. - 请选择档案. + - Cannot open file '%1'. - 不能开启档案 '%1 + - Downloading (%1MB) - 下载中(%1MB) + - Network error: %1. - 网路出错: %1. + - Parsing file - 分析档案中 + - - Xz extraction failed. - xz解压缩失败。 - - - - Sorry, this version of Oracle does not support xz compressed files. - 很抱歉,现前Oracle版本不支援xc压缩档案 - - - Failed to open Zip archive: %1. - 未能开启压缩文件: %1. + - Zip extraction failed: the Zip archive doesn't contain exactly one file. - 解压缩失败:这压缩文件拥有超过一个档案. + - Zip extraction failed: %1. - 解压缩失败:%1。 + - Sorry, this version of Oracle does not support zipped files. - 很抱歉,现前Oracle版本不支援压缩档案. + - - Failed to interpret downloaded data. - 不能分析下载资料. + Do you want to try to download a fresh copy of the uncompressed file instead? + - - Do you want to download the uncompressed file instead? - 你是否想另再下载未经压缩的档案? - - - The file was retrieved successfully, but it does not contain any sets data. - 虽然档案成功取回, 但是档案并未含有任何牌组资料. - - - - LoadSpoilersPage - - - Save spoiler database - 储存预览资料库 - - - - XML; spoiler database (*.xml) - XML ; 预览资料库(*.XML) - - - - spoiler - - Spoilers import - 导入预览卡牌 + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + - - Please specify a compatible source for spoiler data. - 请指定预览资料合用来源. - - - Download URL: - 下载URL: - - - - Local file: - Restore default URL - 恢复常用URL - - - - Choose file... - - The spoiler database will be saved at the following location: - 预览资料库将会储藏于以下位置: - - - - Save to a custom path (not recommended) - 保存到自定义路径(不推荐) + The provided URL is not valid. + LoadTokensPage - - Save token database - 储存衍生物资料库 - - - - XML; token database (*.xml) - XML; 衍生物资料库 (*.xml) - - - - tokens + Tokens source selection - - Tokens import - 导入衍生物 + Error + - - Please specify a compatible source for token data. - 请指定衍生物资料合用来源. + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + - Download URL: - 下载URL: - - - - Local file: - Restore default URL - 恢复常用URL - - - - Choose file... - - The token database will be saved at the following location: - 衍生物资料库将会储藏于以下位置: - - - - Save to a custom path (not recommended) - 保存到自定义路径(不推荐) + The provided URL is not valid. + OracleImporter - Dummy set containing tokens - 包含衍生物的虚拟牌组 + OracleWizard - Oracle Importer - Oracle导入器 - - - - OutroPage - - - Finished - 完成 + - - The wizard has finished. - 向导程式工作完成. - - - - You can now start using Cockatrice with the newly updated cards. - Cockatrice已载入更新了卡牌, 现在已经可以使用. - - - - If the card databases don't reload automatically, restart the Cockatrice client. - 如果卡牌资料库尚未能自动更新,请把cockatrice用户端重新开启. + Save + SaveSetsPage - - - Error - 出现错误 - - - - No set has been imported. - 没有牌组被导入。 - - - Sets imported - 已导入牌组 + - - A cockatrice database file of %1 MB has been downloaded. - 有%1MB大小的Cockatrice资料库档案已被下载. + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + - - The following sets have been found: - 已找到以下牌组: + Save to the default path (recommended) + - - Press "Save" to store the imported cards in the Cockatrice database. - 要将导入卡牌保存于Cockatrice资料库内请按"储藏". + Error + - - The card database will be saved at the following location: - 卡牌资料库将被储藏于以下位置 + No set has been imported. + - - Save to a custom path (not recommended) - 保存到自定义路径(不推荐) - - - - &Save - &储存 - - - Import finished: %1 cards. - 导入过程完成:成功导入%1张卡牌. + - %1: %2 cards imported - %1:成功导入%2张卡牌. + - Save card database - 储存卡牌资料库 + - XML; card database (*.xml) - XML; 卡牌资料库 (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + - The file could not be saved to %1 - 档案未能储藏于%1。 + - SimpleDownloadFilePage + SaveTokensPage - - Load %1 file + Tokens imported - - %1 file (%1) + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 - - - - - Error - 出现错误 - - - - The provided URL is not valid: - - Downloading (0MB) - 下载中(0MB) - - - - Please choose a file. - - - - - Cannot open file '%1'. - - - - - Downloading (%1MB) - 下载中(%1MB) - - - - Network error: %1. - 网路出错: %1. - - - The file could not be saved to %1 - 档案未能储藏于%1 + UnZip - ZIP operation completed successfully. - 档案压缩成功. + - Failed to initialize or load zlib library. - zlib函式库初始化或导入失败. + - zlib library error. - zlib函式库出现错误. + - Unable to create or open file. - 无法创建或开启档案。 + - Partially corrupted archive. Some files might be extracted. - 本存档已受到局部损坏,部分被压缩档案可能已被解压. + - Corrupted archive. - 存档已受损坏. + - Wrong password. - 密码错误. + - No archive has been created yet. - 存档尚未创建. + - File or directory does not exist. - 档案或目录并不存在. + - File read error. - 档案读取出错. + - File write error. - 档案写入出错. + - File seek error. - 檔案查找出錯. + - Unable to create a directory. - 无法创建目录. + - Invalid device. - 装置无效. + - Invalid or incompatible zip archive. - 无效或不配合的压缩存档. + - Inconsistent headers. Archive might be corrupted. - 本档案标题并不一致.本存档可能以受损坏. + - Unknown error. - 不知名错误. + Zip - ZIP operation completed successfully. - 档案压缩成功. + - Failed to initialize or load zlib library. - zlib函式库初始化或导入失败. + - zlib library error. - zlib函式库出现错误. + - Unable to create or open file. - 无法创建或开启档案。 + - No archive has been created yet. - 存档尚未创建. + - File or directory does not exist. - 档案或目录并不存在. + - File read error. - 档案读取出错. + - File write error. - 档案写入出错. + - File seek error. - 档案寻找出错. + - Unknown error. - 无法创建目录. - - - - i18n - - - English - 简体中文 (Chinese Simplified) - - - - main - - - Only run in spoiler mode - 限于在预览模式运行 - - - - Run in no-confirm background mode diff --git a/oracle/translations/oracle_zh-Hant.ts b/oracle/translations/oracle_zh-Hant.ts new file mode 100644 index 000000000..2480716d5 --- /dev/null +++ b/oracle/translations/oracle_zh-Hant.ts @@ -0,0 +1,380 @@ + + + IntroPage + + Introduction + + + + English + 繁體中文 (Chinese Traditional) + + + Language: + + + + This wizard will import the list of sets, cards, and tokens that will be used by Cockatrice. +You will need to specify a URL or a filename that will be used as a source. + + + + + LoadSetsPage + + Source selection + + + + Local file: + + + + Choose file... + + + + Load sets file + + + + Sets JSON file (*.json *.zip) + + + + Sets JSON file (*.json) + + + + Error + + + + Downloading (0MB) + + + + Please choose a file. + + + + Cannot open file '%1'. + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Parsing file + + + + Failed to open Zip archive: %1. + + + + Zip extraction failed: the Zip archive doesn't contain exactly one file. + + + + Zip extraction failed: %1. + + + + Sorry, this version of Oracle does not support zipped files. + + + + Do you want to try to download a fresh copy of the uncompressed file instead? + + + + The file was retrieved successfully, but it does not contain any sets data. + + + + Please specify a source for the list of sets and cards. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + LoadTokensPage + + Tokens source selection + + + + Error + + + + Downloading (0MB) + + + + Downloading (%1MB) + + + + Network error: %1. + + + + Please specify a source for the list of tokens. You can specify a URL address that will be downloaded or use an existing file from your computer. + + + + Download URL: + + + + Restore default URL + + + + The provided URL is not valid. + + + + + OracleImporter + + Dummy set containing tokens + + + + + OracleWizard + + Oracle Importer + + + + Save + + + + + SaveSetsPage + + Sets imported + + + + The following sets has been imported. Press "Save" to save the imported cards to the Cockatrice database. + + + + Save to the default path (recommended) + + + + Error + + + + No set has been imported. + + + + Import finished: %1 cards. + + + + %1: %2 cards imported + + + + Save card database + + + + XML; card database (*.xml) + + + + Success + + + + The card database has been saved successfully to +%1 + + + + The file could not be saved to %1 + + + + + SaveTokensPage + + Tokens imported + + + + The tokens has been imported. Press "Save" to save the imported tokens to the Cockatrice tokens database. + + + + Save to the default path (recommended) + + + + Save token database + + + + XML; token database (*.xml) + + + + Success + + + + The token database has been saved successfully to +%1 + + + + Error + + + + The file could not be saved to %1 + + + + + UnZip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + Partially corrupted archive. Some files might be extracted. + + + + Corrupted archive. + + + + Wrong password. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unable to create a directory. + + + + Invalid device. + + + + Invalid or incompatible zip archive. + + + + Inconsistent headers. Archive might be corrupted. + + + + Unknown error. + + + + + Zip + + ZIP operation completed successfully. + + + + Failed to initialize or load zlib library. + + + + zlib library error. + + + + Unable to create or open file. + + + + No archive has been created yet. + + + + File or directory does not exist. + + + + File read error. + + + + File write error. + + + + File seek error. + + + + Unknown error. + + + + \ No newline at end of file diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index 6e4191beb..d3522fd33 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -2,11 +2,11 @@ # # provides the servatrice binary -project(Servatrice VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +PROJECT(servatrice) -set(servatrice_SOURCES - src/email_parser.cpp +SET(servatrice_SOURCES src/main.cpp + src/passwordhasher.cpp src/servatrice.cpp src/servatrice_connection_pool.cpp src/servatrice_database_interface.cpp @@ -26,205 +26,200 @@ set(servatrice_SOURCES set(servatrice_RESOURCES servatrice.qrc) if(WIN32) - set(servatrice_SOURCES ${servatrice_SOURCES} servatrice.rc) + set(servatrice_SOURCES ${servatrice_SOURCES} servatrice.rc) endif(WIN32) -# Under FreeBSD we need libexecinfo to use backtrace_symbols_fd() -if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") - find_package(Libexecinfo REQUIRED) - set(SYSTEM_LIBRARIES ${EXECINFO_LIBRARY} ${SYSTEM_LIBRARIES}) -endif() if(APPLE) - set(MACOSX_BUNDLE_ICON_FILE appicon.icns) - set_source_files_properties( - ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources - ) - set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) -endif(APPLE) + set(MACOSX_BUNDLE_ICON_FILE appicon.icns) + set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set(servatrice_SOURCES ${servatrice_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/resources/appicon.icns) +ENDIF(APPLE) -if(Qt6_FOUND) - qt6_add_resources(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) -elseif(Qt5_FOUND) - qt5_add_resources(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) +set(SERVATRICE_LIBS) + +# Qt4 stuff +if(Qt4_FOUND) + SET(QT_USE_QTNETWORK TRUE) + SET(QT_USE_QTSQL TRUE) + + # Include directories + INCLUDE(${QT_USE_FILE}) + include_directories(${QT_INCLUDES}) + LIST(APPEND SERVATRICE_LIBS ${QT_LIBRARIES}) + + # Libgcrypt is required only with Qt4 to support SHA512 hashing + FIND_PACKAGE(Libgcrypt REQUIRED) + INCLUDE_DIRECTORIES(${LIBGCRYPT_INCLUDE_DIR}) + QT4_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) endif() -set(QT_DONT_USE_QTGUI TRUE) +# qt5 stuff +if(Qt5Widgets_FOUND) + include_directories(${Qt5Widgets_INCLUDE_DIRS}) + list(APPEND SERVATRICE_LIBS Widgets) + + # QtNetwork + find_package(Qt5Network) + if(Qt5Network_FOUND) + include_directories(${Qt5Network_INCLUDE_DIRS}) + list(APPEND SERVATRICE_LIBS Network) + endif() + + # QtSql + find_package(Qt5Sql) + if(Qt5Sql_FOUND) + include_directories(${Qt5Sql_INCLUDE_DIRS}) + list(APPEND SERVATRICE_LIBS Sql) + endif() + + QT5_ADD_RESOURCES(servatrice_RESOURCES_RCC ${servatrice_RESOURCES}) + + # guess plugins and libraries directory + set(QT_PLUGINS_DIR "${Qt5Widgets_DIR}/../../../plugins") + get_target_property(QT_LIBRARY_DIR Qt5::Core LOCATION) + get_filename_component(QT_LIBRARY_DIR ${QT_LIBRARY_DIR} PATH) +endif() + +SET(QT_DONT_USE_QTGUI TRUE) # Mysql connector if(UNIX) - if(APPLE) - set(MYSQLCLIENT_DEFAULT_PATHS "/usr/local/lib" "/opt/local/lib/mysql55/mysql/" "/opt/local/lib/mysql56/mysql/") - else() - set(MYSQLCLIENT_DEFAULT_PATHS "/usr/lib64" "/usr/local/lib64" "/usr/lib" "/usr/local/lib") - endif() + if(APPLE) + SET(MYSQLCLIENT_DEFAULT_PATHS "/usr/local/lib" "/opt/local/lib/mysql55/mysql/" "/opt/local/lib/mysql56/mysql/") + else() + SET(MYSQLCLIENT_DEFAULT_PATHS "/usr/lib" "/usr/local/lib") + endif() elseif(WIN32) - set(MYSQLCLIENT_DEFAULT_PATHS "C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib" - "C:\\Program Files (x86)\\MySQL\\MySQL Server 5.7\\lib" - ) + SET(MYSQLCLIENT_DEFAULT_PATHS "C:\\Program Files\\MySQL\\MySQL Server 5.5\\lib" "C:\\Program Files\\MySQL\\MySQL Server 5.6\\lib") endif() -find_library( - MYSQL_CLIENT_LIBRARIES - NAMES mysqlclient - PATHS ${MYSQLCLIENT_DEFAULT_PATHS} - PATH_SUFFIXES mysql mariadb -) -if(${MYSQL_CLIENT_LIBRARIES} MATCHES "NOTFOUND") - set(MYSQLCLIENT_FOUND - FALSE - CACHE INTERNAL "" - ) - message(STATUS "MySQL connector NOT FOUND: Servatrice won't be able to connect to a MySQL server") - unset(MYSQL_CLIENT_LIBRARIES) +find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATHS ${MYSQLCLIENT_DEFAULT_PATHS} PATH_SUFFIXES mysql mariadb) +if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND") + set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "") + MESSAGE(STATUS "Mysql connector NOT FOUND: servatrice won't be able to connect to a mysql server") + unset(MYSQLCLIENT_LIBRARIES) else() - set(MYSQLCLIENT_FOUND - TRUE - CACHE INTERNAL "" - ) - get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQL_CLIENT_LIBRARIES} PATH) - message(STATUS "Found MySQL connector at: ${MYSQL_CLIENT_LIBRARIES}") + set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "") + get_filename_component(MYSQLCLIENT_LIBRARY_DIR ${MYSQLCLIENT_LIBRARIES} PATH) + MESSAGE(STATUS "Mysql connector found at: ${MYSQLCLIENT_LIBRARY_DIR}") endif() # Declare path variables -set(ICONDIR - share/icons - CACHE STRING "icon dir" -) -set(DESKTOPDIR - share/applications - CACHE STRING "desktop file destination" -) +set(ICONDIR share/icons CACHE STRING "icon dir") +set(DESKTOPDIR share/applications CACHE STRING "desktop file destination") + +# Include directories +INCLUDE_DIRECTORIES(../common) +INCLUDE_DIRECTORIES(${PROTOBUF_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/../common) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) # Build servatrice binary and link it -add_executable(servatrice MACOSX_BUNDLE ${servatrice_MOC_SRCS} ${servatrice_RESOURCES_RCC} ${servatrice_SOURCES}) +ADD_EXECUTABLE(servatrice MACOSX_BUNDLE ${servatrice_SOURCES} ${servatrice_RESOURCES_RCC} ${servatrice_MOC_SRCS}) +set_property(TARGET servatrice PROPERTY CXX_STANDARD 11) +set_property(TARGET servatrice PROPERTY CXX_STANDARD_REQUIRED ON) -if(CMAKE_HOST_SYSTEM MATCHES "FreeBSD") - target_link_libraries( - servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} - ${LIBEXECINFO_LIBRARY} - ) -else() - target_link_libraries( - servatrice libcockatrice_deck_list libcockatrice_network_server_remote Threads::Threads ${SERVATRICE_QT_MODULES} - ) +if(Qt4_FOUND) + if(MSVC) + set(QT_USE_QTMAIN true) + endif() + TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${SERVATRICE_LIBS} ${LIBGCRYPT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) +endif() +if(Qt5Widgets_FOUND) + if(MSVC) + TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${LIBGCRYPT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} Qt5::WinMain) + else() + TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${LIBGCRYPT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) + endif() + qt5_use_modules(servatrice ${SERVATRICE_LIBS}) endif() # install rules if(UNIX) - if(APPLE) - set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") - set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") - set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) - set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) - set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) + if(APPLE) + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.cockatrice.${PROJECT_NAME}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) + set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) + set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) - install(TARGETS servatrice BUNDLE DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./servatrice.app/Contents/Resources/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./servatrice.app/Contents/Resources/) - else() - # Assume linux - install(TARGETS servatrice RUNTIME DESTINATION bin/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION share/servatrice/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION share/servatrice/) + INSTALL(TARGETS servatrice BUNDLE DESTINATION ./) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./servatrice.app/Contents/Resources/) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./servatrice.app/Contents/Resources/) + else() + # Assume linux + INSTALL(TARGETS servatrice RUNTIME DESTINATION bin/) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION share/servatrice/) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION share/servatrice/) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.desktop DESTINATION ${DESKTOPDIR}) - endif() + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/servatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.desktop DESTINATION ${DESKTOPDIR}) + endif() elseif(WIN32) - install(TARGETS servatrice RUNTIME DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./) + INSTALL(TARGETS servatrice RUNTIME DESTINATION ./) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.ini.example DESTINATION ./) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/servatrice.sql DESTINATION ./) endif() if(APPLE) - # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir servatrice.app/Contents/Plugins) - set(qtconf_dest_dir servatrice.app/Contents/Resources) + # these needs to be relative to CMAKE_INSTALL_PREFIX + set(plugin_dest_dir servatrice.app/Contents/Plugins) + set(qtconf_dest_dir servatrice.app/Contents/Resources) + get_filename_component(QT_LIBRARY_DIR "${QT_LIBRARY_DIR}/.." ABSOLUTE) - # Qt plugins: platforms, sqldrivers/mysql, tls (Qt6) - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "*.dSYM" EXCLUDE - PATTERN "*_debug.dylib" EXCLUDE - PATTERN "platforms/*.dylib" - PATTERN "sqldrivers/libqsqlmysql*.dylib" - PATTERN "tls/*.dylib" - ) + # qt4: codecs, sqldrivers + # qt5: platforms, sqldrivers - install( - CODE " + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime + FILES_MATCHING REGEX "(codecs/.*|platforms/.*|sqldrivers/libqsqlmysql)\\.dylib" + REGEX ".*_debug\\.dylib" EXCLUDE) + + install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] Plugins = Plugins Translations = Resources/translations\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) - install( - CODE " + install(CODE " file(GLOB_RECURSE QTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dylib\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.app\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) endif() if(WIN32) - # these needs to be relative to CMAKE_INSTALL_PREFIX - set(plugin_dest_dir Plugins) - set(qtconf_dest_dir .) + # these needs to be relative to CMAKE_INSTALL_PREFIX + set(plugin_dest_dir Plugins) + set(qtconf_dest_dir .) - install( - DIRECTORY "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/${CMAKE_BUILD_TYPE}/" - DESTINATION ./ - FILES_MATCHING - PATTERN "*.dll" - ) + # qt4: codecs, sqldrivers + # qt5: platforms, sqldrivers - # Qt plugins: platforms, sqldrivers, tls (Qt6) - install( - DIRECTORY "${QT_PLUGINS_DIR}/" - DESTINATION ${plugin_dest_dir} - COMPONENT Runtime - FILES_MATCHING - PATTERN "platforms/qdirect2d.dll" - PATTERN "platforms/qminimal.dll" - PATTERN "platforms/qoffscreen.dll" - PATTERN "platforms/qwindows.dll" - PATTERN "tls/qcertonlybackend.dll" - PATTERN "tls/qopensslbackend.dll" - PATTERN "tls/qschannelbackend.dll" - PATTERN "sqldrivers/qsqlite.dll" - PATTERN "sqldrivers/qsqlodbc.dll" - PATTERN "sqldrivers/qsqlpsql.dll" - ) + install(DIRECTORY "${QT_PLUGINS_DIR}/" DESTINATION ${plugin_dest_dir} COMPONENT Runtime + FILES_MATCHING REGEX "(codecs/.*|platforms/.*|sqldrivers/libqsqlmysql)\\.dll" + REGEX ".*d\\.dll" EXCLUDE) - install( - CODE " + install(CODE " file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${qtconf_dest_dir}/qt.conf\" \"[Paths] Plugins = Plugins Translations = Resources/translations\") - " - COMPONENT Runtime - ) + " COMPONENT Runtime) - install( - CODE " + install(CODE " file(GLOB_RECURSE QTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${plugin_dest_dir}/*.dll\") set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/Servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") - " - COMPONENT Runtime - ) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/servatrice.exe\" \"\${QTPLUGINS}\" \"${QT_LIBRARY_DIR};${MYSQLCLIENT_LIBRARY_DIR}\") + " COMPONENT Runtime) +endif() +#Compile a portable version, default off +option(PORTABLE "portable build" OFF) +IF(PORTABLE) +add_definitions(-DPORTABLE_BUILD) endif() diff --git a/servatrice/check_schema_version.sh b/servatrice/check_schema_version.sh index c4aadf356..d59edc7a2 100755 --- a/servatrice/check_schema_version.sh +++ b/servatrice/check_schema_version.sh @@ -2,16 +2,13 @@ set -e -version_line="$(grep 'INSERT INTO cockatrice_schema_version' servatrice/servatrice.sql)" -version_line="${version_line#*VALUES(}" -declare -i schema_ver="${version_line%%)*}" +schema_ver="$(grep 'INSERT INTO cockatrice_schema_version' servatrice/servatrice.sql | sed 's/.*VALUES(//' | sed 's/).*//')" -# shellcheck disable=2012 latest_migration="$(ls -1 servatrice/migrations/ | tail -n1)" xtoysql="${latest_migration#servatrice_}" xtoy="${xtoysql%.sql}" -declare -i old_ver="10#${xtoy%_to_*}" #declare as integer with base 10, numbers with a leading 0 are normally interpreted as base 16 -declare -i new_ver="10#${xtoy#*_to_}" +old_ver="$(echo ${xtoy%%_to_*} | bc)" +new_ver="$(echo ${xtoy##*_to_} | bc)" if ((old_ver >= new_ver)); then echo "New version $new_ver is not newer than $old_ver" @@ -24,7 +21,7 @@ if ((schema_ver != new_ver)); then fi expected_sql="^UPDATE cockatrice_schema_version SET version=${new_ver} WHERE version=${old_ver};$" -if ! grep -q "$expected_sql" "servatrice/migrations/$latest_migration"; then +if ! grep -q "$expected_sql" servatrice/migrations/$latest_migration; then echo "$latest_migration does not contain expected sql: $expected_sql" exit 1 fi diff --git a/servatrice/docker/servatrice-docker.ini b/servatrice/docker/servatrice-docker.ini deleted file mode 100644 index 59af1caf9..000000000 --- a/servatrice/docker/servatrice-docker.ini +++ /dev/null @@ -1,7 +0,0 @@ -[database] -type=mysql -prefix=cockatrice -hostname=mysql -database=servatrice -user=servatrice -password=password diff --git a/servatrice/migrations/servatrice_0012_to_0013.sql b/servatrice/migrations/servatrice_0012_to_0013.sql deleted file mode 100644 index 788932810..000000000 --- a/servatrice/migrations/servatrice_0012_to_0013.sql +++ /dev/null @@ -1,86 +0,0 @@ --- Servatrice db migration from version 12 to version 13 - --- WARNING: this is quite a big change, so you really, REALLY should --- backup your database before attempting to execute this migration. - --- First move all the tables to the InnoDB engine -ALTER TABLE `cockatrice_schema_version` ENGINE=InnoDB; -ALTER TABLE `cockatrice_decklist_files` ENGINE=InnoDB; -ALTER TABLE `cockatrice_decklist_folders` ENGINE=InnoDB; -ALTER TABLE `cockatrice_games` ENGINE=InnoDB; -ALTER TABLE `cockatrice_games_players` ENGINE=InnoDB; -ALTER TABLE `cockatrice_news` ENGINE=InnoDB; -ALTER TABLE `cockatrice_users` ENGINE=InnoDB; -ALTER TABLE `cockatrice_uptime` ENGINE=InnoDB; -ALTER TABLE `cockatrice_servermessages` ENGINE=InnoDB; -ALTER TABLE `cockatrice_ignorelist` ENGINE=InnoDB; -ALTER TABLE `cockatrice_buddylist` ENGINE=InnoDB; -ALTER TABLE `cockatrice_bans` ENGINE=InnoDB; -ALTER TABLE `cockatrice_warnings` ENGINE=InnoDB; -ALTER TABLE `cockatrice_sessions` ENGINE=InnoDB; -ALTER TABLE `cockatrice_servers` ENGINE=InnoDB; -ALTER TABLE `cockatrice_replays` ENGINE=InnoDB; -ALTER TABLE `cockatrice_replays_access` ENGINE=InnoDB; -ALTER TABLE `cockatrice_rooms` ENGINE=InnoDB; -ALTER TABLE `cockatrice_rooms_gametypes` ENGINE=InnoDB; -ALTER TABLE `cockatrice_log` ENGINE=InnoDB; -ALTER TABLE `cockatrice_activation_emails` ENGINE=InnoDB; -ALTER TABLE `cockatrice_user_analytics` ENGINE=InnoDB; - --- Fix the replays tables not using unsigned values for id_game and id_player -ALTER TABLE `cockatrice_replays` MODIFY COLUMN `id_game` int(7) unsigned NULL; -ALTER TABLE `cockatrice_replays_access` MODIFY COLUMN `id_game` int(7) unsigned NOT NULL; -ALTER TABLE `cockatrice_replays_access` MODIFY COLUMN `id_player` int(7) unsigned NOT NULL; - --- Now add some foreign keys between tables. Since there was no constaint before, --- we need to ensure no leftover record (eg. a user deck without an user) exists --- before adding the FK, or the query will fail. - -DELETE FROM `cockatrice_decklist_files` WHERE `id_user` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_decklist_files` ADD FOREIGN KEY(`id_user`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_decklist_folders` WHERE `id_user` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_decklist_folders` ADD FOREIGN KEY(`id_user`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_news` WHERE `id_user` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_news` ADD FOREIGN KEY(`id_user`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_ignorelist` WHERE `id_user1` NOT IN (SELECT `id` FROM `cockatrice_users`); -DELETE FROM `cockatrice_ignorelist` WHERE `id_user2` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_ignorelist` ADD FOREIGN KEY(`id_user1`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE `cockatrice_ignorelist` ADD FOREIGN KEY(`id_user2`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_buddylist` WHERE `id_user1` NOT IN (SELECT `id` FROM `cockatrice_users`); -DELETE FROM `cockatrice_buddylist` WHERE `id_user2` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_buddylist` ADD FOREIGN KEY(`id_user1`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; -ALTER TABLE `cockatrice_buddylist` ADD FOREIGN KEY(`id_user2`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_user_analytics` WHERE `id` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_user_analytics` ADD FOREIGN KEY(`id`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_log` WHERE `sender_id` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_log` ADD FOREIGN KEY(`sender_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_activation_emails` WHERE `name` NOT IN (SELECT `name` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_activation_emails` ADD FOREIGN KEY(`name`) REFERENCES `cockatrice_users`(`name`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_rooms_gametypes` WHERE `id_room` NOT IN (SELECT `id` FROM `cockatrice_rooms`); -ALTER TABLE `cockatrice_rooms_gametypes` ADD FOREIGN KEY(`id_room`) REFERENCES `cockatrice_rooms`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_games_players` WHERE `id_game` NOT IN (SELECT `id` FROM `cockatrice_games`); -ALTER TABLE `cockatrice_games_players` ADD FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_replays` WHERE `id_game` NOT IN (SELECT `id` FROM `cockatrice_games`); -ALTER TABLE `cockatrice_replays` ADD FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_replays_access` WHERE `id_game` NOT IN (SELECT `id` FROM `cockatrice_games`); -ALTER TABLE `cockatrice_replays_access` ADD FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_replays_access` WHERE `id_player` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_replays_access` ADD FOREIGN KEY(`id_player`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - -DELETE FROM `cockatrice_bans` WHERE `id_admin` NOT IN (SELECT `id` FROM `cockatrice_users`); -ALTER TABLE `cockatrice_bans` ADD FOREIGN KEY(`id_admin`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; - --- Last: update schema version -UPDATE cockatrice_schema_version SET version=13 WHERE version=12; diff --git a/servatrice/migrations/servatrice_0013_to_0014.sql b/servatrice/migrations/servatrice_0013_to_0014.sql deleted file mode 100644 index 9b8be3f2a..000000000 --- a/servatrice/migrations/servatrice_0013_to_0014.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Servatrice db migration from version 13 to version 14 - -alter table cockatrice_sessions add `connection_type` ENUM('tcp', 'websocket'); -UPDATE cockatrice_sessions SET connection_type = 'tcp'; - -UPDATE cockatrice_schema_version SET version=14 WHERE version=13; diff --git a/servatrice/migrations/servatrice_0014_to_0015.sql b/servatrice/migrations/servatrice_0014_to_0015.sql deleted file mode 100644 index bb83d5836..000000000 --- a/servatrice/migrations/servatrice_0014_to_0015.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Servatrice db migration from version 14 to version 15 - -alter table cockatrice_rooms add `id_server` tinyint(3) not null default 0; -alter table cockatrice_rooms_gametypes add `id_server` tinyint(3) not null default 0; - -UPDATE cockatrice_schema_version SET version=15 WHERE version=14; diff --git a/servatrice/migrations/servatrice_0015_to_0016.sql b/servatrice/migrations/servatrice_0015_to_0016.sql deleted file mode 100644 index d6029c50a..000000000 --- a/servatrice/migrations/servatrice_0015_to_0016.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 15 to version 16 - -drop table cockatrice_news; - -UPDATE cockatrice_schema_version SET version=16 WHERE version=15; diff --git a/servatrice/migrations/servatrice_0016_to_0017.sql b/servatrice/migrations/servatrice_0016_to_0017.sql deleted file mode 100644 index c351c4c7a..000000000 --- a/servatrice/migrations/servatrice_0016_to_0017.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Servatrice db migration from version 16 to version 17 - -alter table cockatrice_rooms modify column `id_server` tinyint(3) not null default 1; -alter table cockatrice_rooms_gametypes modify column `id_server` tinyint(3) not null default 1; -alter table cockatrice_servermessages modify column `id_server` tinyint(3) not null default 1; - -UPDATE cockatrice_schema_version SET version=17 WHERE version=16; diff --git a/servatrice/migrations/servatrice_0017_to_0018.sql b/servatrice/migrations/servatrice_0017_to_0018.sql deleted file mode 100644 index 5b61f9bbc..000000000 --- a/servatrice/migrations/servatrice_0017_to_0018.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 17 to version 18 - -alter table cockatrice_users add column privlevel enum("NONE","VIP","DONATOR") NOT NULL; - -UPDATE cockatrice_schema_version SET version=18 WHERE version=17; diff --git a/servatrice/migrations/servatrice_0018_to_0019.sql b/servatrice/migrations/servatrice_0018_to_0019.sql deleted file mode 100644 index 2c936307d..000000000 --- a/servatrice/migrations/servatrice_0018_to_0019.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Servatrice db migration from version 18 to version 19 - -alter table cockatrice_sessions modify column `user_name` varchar(35) NOT NULL; -alter table cockatrice_sessions modify column `ip_address` varchar(255) NOT NULL; - -UPDATE cockatrice_schema_version SET version=19 WHERE version=18; diff --git a/servatrice/migrations/servatrice_0019_to_0020.sql b/servatrice/migrations/servatrice_0019_to_0020.sql deleted file mode 100644 index 337e27fb2..000000000 --- a/servatrice/migrations/servatrice_0019_to_0020.sql +++ /dev/null @@ -1,20 +0,0 @@ --- Servatrice db migration from version 19 to version 20 - -alter table cockatrice_users add column privlevelStartDate datetime NOT NULL; -alter table cockatrice_users add column privlevelEndDate datetime NOT NULL; -update cockatrice_users set privlevelStartDate = NOW() where privlevel != 'NONE'; -update cockatrice_users set privlevelEndDate = DATE_ADD(NOW() , INTERVAL 30 DAY) where privlevel != 'NONE'; - -CREATE TABLE IF NOT EXISTS `cockatrice_donations` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(255) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `payment_pre_fee` double DEFAULT NULL, - `payment_post_fee` double DEFAULT NULL, - `term_length` int(11) DEFAULT NULL, - `date` varchar(255) DEFAULT NULL, - `pp_type` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -UPDATE cockatrice_schema_version SET version=20 WHERE version=19; diff --git a/servatrice/migrations/servatrice_0020_to_0021.sql b/servatrice/migrations/servatrice_0020_to_0021.sql deleted file mode 100644 index 97372253b..000000000 --- a/servatrice/migrations/servatrice_0020_to_0021.sql +++ /dev/null @@ -1,12 +0,0 @@ - - -CREATE TABLE IF NOT EXISTS `cockatrice_forgot_password` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `name` varchar(35) NOT NULL, - `requestDate` datetime NOT NULL default '0000-00-00 00:00:00', - `emailed` tinyint(1) NOT NULL default 0, - PRIMARY KEY (`id`), - KEY `user_name` (`name`) -) ENGINE=INNODB DEFAULT CHARSET=utf8; - -UPDATE cockatrice_schema_version SET version=21 WHERE version=20; \ No newline at end of file diff --git a/servatrice/migrations/servatrice_0021_to_0022.sql b/servatrice/migrations/servatrice_0021_to_0022.sql deleted file mode 100644 index 10ed30785..000000000 --- a/servatrice/migrations/servatrice_0021_to_0022.sql +++ /dev/null @@ -1,16 +0,0 @@ - -CREATE TABLE IF NOT EXISTS `cockatrice_audit` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `id_server` tinyint(3) NOT NULL, - `name` varchar(35) NOT NULL, - `ip_address` varchar(255) NOT NULL, - `clientid` varchar(15) NOT NULL, - `incidentDate` datetime NOT NULL default '0000-00-00 00:00:00', - `action` varchar(35) NOT NULL, - `results` ENUM('fail', 'success') NOT NULL DEFAULT 'fail', - `details` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - KEY `user_name` (`name`) -) ENGINE=INNODB DEFAULT CHARSET=utf8; - -UPDATE cockatrice_schema_version SET version=22 WHERE version=21; \ No newline at end of file diff --git a/servatrice/migrations/servatrice_0022_to_0023.sql b/servatrice/migrations/servatrice_0022_to_0023.sql deleted file mode 100644 index 12109afad..000000000 --- a/servatrice/migrations/servatrice_0022_to_0023.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Servatrice db migration from version 22 to version 23 - -alter table cockatrice_rooms modify column permissionlevel enum('NONE','REGISTERED','MODERATOR','ADMINISTRATOR'); -alter table cockatrice_rooms add column privlevel enum('NONE','PRIVILEGED','VIP','DONATOR') NOT NULL; - -UPDATE cockatrice_schema_version SET version=23 WHERE version=22; diff --git a/servatrice/migrations/servatrice_0023_to_0024.sql b/servatrice/migrations/servatrice_0023_to_0024.sql deleted file mode 100644 index 5d60d8541..000000000 --- a/servatrice/migrations/servatrice_0023_to_0024.sql +++ /dev/null @@ -1,62 +0,0 @@ --- Servatrice db migration from version 23 to version 24 - -SET FOREIGN_KEY_CHECKS=0; - --- short the "ip address" columns to 45 chars (max length of an ipv6 address) --- to ensure the field can be used as a key on mysql < 5.7 --- (not all fields are actually keys, but better keep them uniform) -ALTER TABLE `cockatrice_sessions` MODIFY COLUMN `ip_address` varchar(45) NOT NULL; -ALTER TABLE `cockatrice_bans` MODIFY COLUMN `ip_address` varchar(45) NOT NULL; -ALTER TABLE `cockatrice_log` MODIFY COLUMN `sender_ip` varchar(45) NOT NULL; -ALTER TABLE `cockatrice_audit` MODIFY COLUMN `ip_address` varchar(45) NOT NULL; - --- short the "user name" columns to 35 chars (current max length in servatrice) --- to ensure the field can be used as a key on mysql < 5.7 --- (not all fields are actually keys, but better keep them uniform) -ALTER TABLE `cockatrice_bans` MODIFY COLUMN `user_name` varchar(35) NOT NULL; -ALTER TABLE `cockatrice_warnings` MODIFY COLUMN `user_name` varchar(35) NOT NULL; -ALTER TABLE `cockatrice_warnings` MODIFY COLUMN `mod_name` varchar(35) NOT NULL; -ALTER TABLE `cockatrice_games` MODIFY COLUMN `creator_name` varchar(35) NOT NULL; -ALTER TABLE `cockatrice_games_players` MODIFY COLUMN `player_name` varchar(35) NOT NULL; -ALTER TABLE `cockatrice_donations` MODIFY COLUMN `username` varchar(35) NOT NULL; - --- remove the FK from cockatrice_activation_emails (it will be created again later) --- the key name should end with _1, but multiple run of the 0012_to_003 migration --- can lead to a different name or even multiple keys. In this case you must remove --- all of them before continue. --- Use "show create table cockatrice_activation_emails" to see the key names. -ALTER TABLE cockatrice_activation_emails DROP FOREIGN KEY `cockatrice_activation_emails_ibfk_1`; - --- unify tables and columns collation to utf8mb4_unicode_ci -ALTER TABLE `cockatrice_schema_version` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_decklist_files` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_decklist_folders` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_ignorelist` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_buddylist` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_rooms` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_rooms_gametypes` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_games` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_games_players` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_replays` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_replays_access` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_servers` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_uptime` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_servermessages` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_sessions` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_bans` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_warnings` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_log` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_activation_emails` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_user_analytics` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_donations` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_forgot_password` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -ALTER TABLE `cockatrice_audit` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- re-add the FK constraint on cockatrice_activation_emails -ALTER TABLE `cockatrice_activation_emails` ADD FOREIGN KEY(`name`) REFERENCES `cockatrice_users`(`name`) ON DELETE CASCADE ON UPDATE CASCADE; - -SET FOREIGN_KEY_CHECKS=1; - --- update schema version -UPDATE cockatrice_schema_version SET version=24 WHERE version=23; diff --git a/servatrice/migrations/servatrice_0024_to_0025.sql b/servatrice/migrations/servatrice_0024_to_0025.sql deleted file mode 100644 index 290f15008..000000000 --- a/servatrice/migrations/servatrice_0024_to_0025.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Servatrice db migration from version 24 to version 25 - -ALTER TABLE cockatrice_uptime ADD COLUMN mods_count int(11) NOT NULL DEFAULT 0; -ALTER TABLE cockatrice_uptime ADD COLUMN mods_list TEXT; - -UPDATE cockatrice_schema_version SET version=25 WHERE version=24; diff --git a/servatrice/migrations/servatrice_0025_to_0026.sql b/servatrice/migrations/servatrice_0025_to_0026.sql deleted file mode 100644 index fbbd3ee78..000000000 --- a/servatrice/migrations/servatrice_0025_to_0026.sql +++ /dev/null @@ -1,171 +0,0 @@ --- Servatrice db migration from version 25 to version 26 - --- Some previous migrations didn't care about column ordering, --- meaning select could return info in a different order depending on the --- age of the database. - --- This migration ensures a consistent column ordering across all databases, --- regardless of age. Future migrations should take care to ensure this --- ordering stays consistent. - --- cockatrice_users.id cannot be modified because its used in a foreign key constraint --- it should be first anyway, so this isnt a big deal --- ALTER TABLE cockatrice_users MODIFY `id` int(7) unsigned zerofill NOT NULL FIRST; -ALTER TABLE cockatrice_users MODIFY COLUMN `admin` tinyint(1) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_users MODIFY COLUMN `name` varchar(35) NOT NULL AFTER `admin`; -ALTER TABLE cockatrice_users MODIFY COLUMN `realname` varchar(255) NOT NULL AFTER `name`; -ALTER TABLE cockatrice_users MODIFY COLUMN `gender` char(1) NOT NULL AFTER `realname`; -ALTER TABLE cockatrice_users MODIFY COLUMN `password_sha512` char(120) NOT NULL AFTER `gender`; -ALTER TABLE cockatrice_users MODIFY COLUMN `email` varchar(255) NOT NULL AFTER `password_sha512`; -ALTER TABLE cockatrice_users MODIFY COLUMN `country` char(2) NOT NULL AFTER `email`; -ALTER TABLE cockatrice_users MODIFY COLUMN `avatar_bmp` blob NOT NULL AFTER `country`; -ALTER TABLE cockatrice_users MODIFY COLUMN `registrationDate` datetime NOT NULL AFTER `avatar_bmp`; -ALTER TABLE cockatrice_users MODIFY COLUMN `active` tinyint(1) NOT NULL AFTER `registrationDate`; -ALTER TABLE cockatrice_users MODIFY COLUMN `token` binary(16) AFTER `active`; -ALTER TABLE cockatrice_users MODIFY COLUMN `clientid` varchar(15) NOT NULL AFTER `token`; -ALTER TABLE cockatrice_users MODIFY COLUMN `privlevel` enum("NONE","VIP","DONATOR") NOT NULL AFTER `clientid`; -ALTER TABLE cockatrice_users MODIFY COLUMN `privlevelStartDate` datetime NOT NULL AFTER `privlevel`; -ALTER TABLE cockatrice_users MODIFY COLUMN `privlevelEndDate` datetime NOT NULL AFTER `privlevelStartDate`; - -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `id` int(7) unsigned zerofill NOT NULL auto_increment FIRST; -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `id_folder` int(7) unsigned zerofill NOT NULL AFTER `id`; -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `id_user` int(7) unsigned NULL AFTER `id_folder`; -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `name` varchar(50) NOT NULL AFTER `id_user`; -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `upload_time` datetime NOT NULL AFTER `name`; -ALTER TABLE cockatrice_decklist_files MODIFY COLUMN `content` text NOT NULL AFTER `upload_time`; - -ALTER TABLE cockatrice_decklist_folders MODIFY COLUMN `id` int(7) unsigned zerofill NOT NULL auto_increment FIRST; -ALTER TABLE cockatrice_decklist_folders MODIFY COLUMN `id_parent` int(7) unsigned zerofill NOT NULL AFTER `id`; -ALTER TABLE cockatrice_decklist_folders MODIFY COLUMN `id_user` int(7) unsigned NULL AFTER `id_parent`; -ALTER TABLE cockatrice_decklist_folders MODIFY COLUMN `name` varchar(30) NOT NULL AFTER `id_user`; - -ALTER TABLE cockatrice_ignorelist MODIFY COLUMN `id_user1` int(7) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_ignorelist MODIFY COLUMN `id_user2` int(7) unsigned NOT NULL AFTER `id_user1`; - -ALTER TABLE cockatrice_buddylist MODIFY COLUMN `id_user1` int(7) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_buddylist MODIFY COLUMN `id_user2` int(7) unsigned NOT NULL AFTER `id_user1`; - -ALTER TABLE cockatrice_rooms MODIFY COLUMN `id` int(7) unsigned NOT NULL auto_increment FIRST; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `name` varchar(50) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `descr` varchar(255) NOT NULL AFTER `name`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `permissionlevel` enum('NONE','REGISTERED','MODERATOR','ADMINISTRATOR') NOT NULL AFTER `descr`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `privlevel` enum('NONE','PRIVILEGED','VIP','DONATOR') NOT NULL AFTER `permissionlevel`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `auto_join` tinyint(1) default 0 AFTER `privlevel`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `join_message` varchar(255) NOT NULL AFTER `auto_join`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `chat_history_size` int(4) NOT NULL AFTER `join_message`; -ALTER TABLE cockatrice_rooms MODIFY COLUMN `id_server` tinyint(3) NOT NULL DEFAULT 1 AFTER `chat_history_size`; - -ALTER TABLE cockatrice_rooms_gametypes MODIFY COLUMN `id_room` int(7) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_rooms_gametypes MODIFY COLUMN `name` varchar(50) NOT NULL AFTER `id_room`; -ALTER TABLE cockatrice_rooms_gametypes MODIFY COLUMN `id_server` tinyint(3) NOT NULL DEFAULT 1 AFTER `name`; - -ALTER TABLE cockatrice_games MODIFY COLUMN `room_name` varchar(255) NOT NULL FIRST; -ALTER TABLE cockatrice_games MODIFY COLUMN `id` int(7) unsigned NOT NULL auto_increment AFTER `room_name`; -ALTER TABLE cockatrice_games MODIFY COLUMN `descr` varchar(50) default NULL AFTER `id`; -ALTER TABLE cockatrice_games MODIFY COLUMN `creator_name` varchar(35) NOT NULL AFTER `descr`; -ALTER TABLE cockatrice_games MODIFY COLUMN `password` tinyint(1) NOT NULL AFTER `creator_name`; -ALTER TABLE cockatrice_games MODIFY COLUMN `game_types` varchar(255) NOT NULL AFTER `password`; -ALTER TABLE cockatrice_games MODIFY COLUMN `player_count` tinyint(3) NOT NULL AFTER `game_types`; -ALTER TABLE cockatrice_games MODIFY COLUMN `time_started` datetime default NULL AFTER `player_count`; -ALTER TABLE cockatrice_games MODIFY COLUMN `time_finished` datetime default NULL AFTER `time_started`; - -ALTER TABLE cockatrice_games_players MODIFY COLUMN `id_game` int(7) unsigned zerofill NOT NULL FIRST; -ALTER TABLE cockatrice_games_players MODIFY COLUMN `player_name` varchar(35) NOT NULL AFTER `id_game`; - -ALTER TABLE cockatrice_replays MODIFY COLUMN `id` int(7) NOT NULL AUTO_INCREMENT FIRST; -ALTER TABLE cockatrice_replays MODIFY COLUMN `id_game` int(7) unsigned NULL AFTER `id`; -ALTER TABLE cockatrice_replays MODIFY COLUMN `duration` int(7) NOT NULL AFTER `id_game`; -ALTER TABLE cockatrice_replays MODIFY COLUMN `replay` mediumblob NOT NULL AFTER `duration`; - -ALTER TABLE cockatrice_replays_access MODIFY COLUMN `id_game` int(7) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_replays_access MODIFY COLUMN `id_player` int(7) unsigned NOT NULL AFTER `id_game`; -ALTER TABLE cockatrice_replays_access MODIFY COLUMN `replay_name` varchar(255) NOT NULL AFTER `id_player`; -ALTER TABLE cockatrice_replays_access MODIFY COLUMN `do_not_hide` tinyint(1) NOT NULL AFTER `replay_name`; - -ALTER TABLE cockatrice_servers MODIFY COLUMN `id` mediumint(8) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_servers MODIFY COLUMN `ssl_cert` text NOT NULL AFTER `id`; -ALTER TABLE cockatrice_servers MODIFY COLUMN `hostname` varchar(255) NOT NULL AFTER `ssl_cert`; -ALTER TABLE cockatrice_servers MODIFY COLUMN `address` varchar(255) NOT NULL AFTER `hostname`; -ALTER TABLE cockatrice_servers MODIFY COLUMN `game_port` mediumint(8) unsigned NOT NULL AFTER `address`; -ALTER TABLE cockatrice_servers MODIFY COLUMN `control_port` mediumint(9) NOT NULL AFTER `game_port`; - -ALTER TABLE cockatrice_uptime MODIFY COLUMN `id_server` tinyint(3) NOT NULL FIRST; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `timest` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `id_server`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `uptime` int(11) NOT NULL AFTER `timest`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `users_count` int(11) NOT NULL AFTER `uptime`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `mods_count` int(11) NOT NULL DEFAULT 0 AFTER `users_count`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `mods_list` TEXT AFTER `mods_count`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `games_count` int(11) NOT NULL AFTER `mods_list`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `rx_bytes` int(11) NOT NULL AFTER `games_count`; -ALTER TABLE cockatrice_uptime MODIFY COLUMN `tx_bytes` int(11) NOT NULL AFTER `rx_bytes`; - -ALTER TABLE cockatrice_servermessages MODIFY COLUMN `id_server` tinyint(3) not null default 1 FIRST; -ALTER TABLE cockatrice_servermessages MODIFY COLUMN `timest` datetime NOT NULL default '0000-00-00 00:00:00' AFTER `id_server`; -ALTER TABLE cockatrice_servermessages MODIFY COLUMN `message` text AFTER `timest`; - -ALTER TABLE cockatrice_sessions MODIFY COLUMN `id` int(9) NOT NULL AUTO_INCREMENT FIRST; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `user_name` varchar(35) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `id_server` tinyint(3) NOT NULL AFTER `user_name`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `ip_address` varchar(45) NOT NULL AFTER `id_server`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `start_time` datetime NOT NULL AFTER `ip_address`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `end_time` datetime DEFAULT NULL AFTER `start_time`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `clientid` varchar(15) NOT NULL AFTER `end_time`; -ALTER TABLE cockatrice_sessions MODIFY COLUMN `connection_type` ENUM('tcp', 'websocket') AFTER `clientid`; - -ALTER TABLE cockatrice_bans MODIFY COLUMN `user_name` varchar(35) NOT NULL FIRST; -ALTER TABLE cockatrice_bans MODIFY COLUMN `ip_address` varchar(45) NOT NULL AFTER `user_name`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `id_admin` int(7) unsigned zerofill NOT NULL AFTER `ip_address`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `time_from` datetime NOT NULL AFTER `id_admin`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `minutes` int(6) NOT NULL AFTER `time_from`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `reason` text NOT NULL AFTER `minutes`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `visible_reason` text NOT NULL AFTER `reason`; -ALTER TABLE cockatrice_bans MODIFY COLUMN `clientid` varchar(15) NOT NULL AFTER `visible_reason`; - -ALTER TABLE cockatrice_warnings MODIFY COLUMN `user_id` int(7) unsigned NOT NULL FIRST; -ALTER TABLE cockatrice_warnings MODIFY COLUMN `user_name` varchar(35) NOT NULL AFTER `user_id`; -ALTER TABLE cockatrice_warnings MODIFY COLUMN `mod_name` varchar(35) NOT NULL AFTER `user_name`; -ALTER TABLE cockatrice_warnings MODIFY COLUMN `reason` text NOT NULL AFTER `mod_name`; -ALTER TABLE cockatrice_warnings MODIFY COLUMN `time_of` datetime NOT NULL AFTER `reason`; -ALTER TABLE cockatrice_warnings MODIFY COLUMN `clientid` varchar(15) NOT NULL AFTER `time_of`; - -ALTER TABLE cockatrice_log MODIFY COLUMN `log_time` datetime NOT NULL FIRST; -ALTER TABLE cockatrice_log MODIFY COLUMN `sender_id` int(7) unsigned NULL AFTER `log_time`; -ALTER TABLE cockatrice_log MODIFY COLUMN `sender_name` varchar(35) NOT NULL AFTER `sender_id`; -ALTER TABLE cockatrice_log MODIFY COLUMN `sender_ip` varchar(45) NOT NULL AFTER `sender_name`; -ALTER TABLE cockatrice_log MODIFY COLUMN `log_message` text NOT NULL AFTER `sender_ip`; -ALTER TABLE cockatrice_log MODIFY COLUMN `target_type` ENUM('room', 'game', 'chat') AFTER `log_message`; -ALTER TABLE cockatrice_log MODIFY COLUMN `target_id` int(7) NULL AFTER `target_type`; -ALTER TABLE cockatrice_log MODIFY COLUMN `target_name` varchar(50) NOT NULL AFTER `target_id`; - --- cockatrice_activation_emails has only 1 column so we skip it - -ALTER TABLE cockatrice_user_analytics MODIFY COLUMN `id` int(7) unsigned zerofill NOT NULL FIRST; -ALTER TABLE cockatrice_user_analytics MODIFY COLUMN `client_ver` varchar(35) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_user_analytics MODIFY COLUMN `last_login` datetime NOT NULL AFTER `client_ver`; -ALTER TABLE cockatrice_user_analytics MODIFY COLUMN `notes` varchar(255) NOT NULL AFTER `last_login`; - -ALTER TABLE cockatrice_donations MODIFY COLUMN `id` int(11) unsigned NOT NULL AUTO_INCREMENT FIRST; -ALTER TABLE cockatrice_donations MODIFY COLUMN `username` varchar(35) DEFAULT NULL AFTER `id`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `email` varchar(255) DEFAULT NULL AFTER `username`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `payment_pre_fee` double DEFAULT NULL AFTER `email`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `payment_post_fee` double DEFAULT NULL AFTER `payment_pre_fee`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `term_length` int(11) DEFAULT NULL AFTER `payment_post_fee`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `date` varchar(255) DEFAULT NULL AFTER `term_length`; -ALTER TABLE cockatrice_donations MODIFY COLUMN `pp_type` varchar(255) DEFAULT NULL AFTER `date`; - -ALTER TABLE cockatrice_forgot_password MODIFY COLUMN `id` int(7) unsigned zerofill NOT NULL auto_increment FIRST; -ALTER TABLE cockatrice_forgot_password MODIFY COLUMN `name` varchar(35) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_forgot_password MODIFY COLUMN `requestDate` datetime NOT NULL default '0000-00-00 00:00:00' AFTER `name`; -ALTER TABLE cockatrice_forgot_password MODIFY COLUMN `emailed` tinyint(1) NOT NULL default 0 AFTER `requestDate`; - -ALTER TABLE cockatrice_audit MODIFY COLUMN `id` int(7) unsigned zerofill NOT NULL auto_increment FIRST; -ALTER TABLE cockatrice_audit MODIFY COLUMN `id_server` tinyint(3) NOT NULL AFTER `id`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `name` varchar(35) NOT NULL AFTER `id_server`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `ip_address` varchar(45) NOT NULL AFTER `name`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `clientid` varchar(15) NOT NULL AFTER `ip_address`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `incidentDate` datetime NOT NULL default '0000-00-00 00:00:00' AFTER `clientid`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `action` varchar(35) NOT NULL AFTER `incidentDate`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `results` ENUM('fail', 'success') NOT NULL DEFAULT 'fail' AFTER `action`; -ALTER TABLE cockatrice_audit MODIFY COLUMN `details` varchar(255) NOT NULL AFTER `results`; - -UPDATE cockatrice_schema_version SET version=26 WHERE version=25; diff --git a/servatrice/migrations/servatrice_0026_to_0027.sql b/servatrice/migrations/servatrice_0026_to_0027.sql deleted file mode 100644 index bed0c488b..000000000 --- a/servatrice/migrations/servatrice_0026_to_0027.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 26 to version 27 - -ALTER TABLE cockatrice_users ADD COLUMN passwordLastChangedDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00'; - -UPDATE cockatrice_schema_version SET version=27 WHERE version=26; diff --git a/servatrice/migrations/servatrice_0027_to_0028.sql b/servatrice/migrations/servatrice_0027_to_0028.sql deleted file mode 100644 index 70bec3607..000000000 --- a/servatrice/migrations/servatrice_0027_to_0028.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 27 to version 28 - -ALTER TABLE cockatrice_users DROP COLUMN gender; - -UPDATE cockatrice_schema_version SET version=28 WHERE version=27; diff --git a/servatrice/migrations/servatrice_0028_to_0029.sql b/servatrice/migrations/servatrice_0028_to_0029.sql deleted file mode 100644 index 03cfcfa71..000000000 --- a/servatrice/migrations/servatrice_0028_to_0029.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 28 to version 29 - -ALTER TABLE cockatrice_users MODIFY COLUMN avatar_bmp mediumblob NOT NULL; - -UPDATE cockatrice_schema_version SET version=29 WHERE version=28; diff --git a/servatrice/migrations/servatrice_0029_to_0030.sql b/servatrice/migrations/servatrice_0029_to_0030.sql deleted file mode 100644 index 37f32ccb0..000000000 --- a/servatrice/migrations/servatrice_0029_to_0030.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 29 to version 30 - -ALTER TABLE cockatrice_users ADD COLUMN adminnotes mediumtext NOT NULL; - -UPDATE cockatrice_schema_version SET version=30 WHERE version=29; diff --git a/servatrice/migrations/servatrice_0030_to_0031.sql b/servatrice/migrations/servatrice_0030_to_0031.sql deleted file mode 100644 index 963336ea0..000000000 --- a/servatrice/migrations/servatrice_0030_to_0031.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Servatrice db migration from version 30 to version 31 - -ALTER TABLE cockatrice_log DROP INDEX `target_type`; - -ALTER TABLE cockatrice_forgot_password ADD INDEX idx_emailed (`emailed`); -ALTER TABLE cockatrice_sessions ADD INDEX idx_start_time (`start_time`); -ALTER TABLE cockatrice_users ADD INDEX idx_admin (`admin`); -ALTER TABLE cockatrice_users ADD INDEX idx_active (`active`); -ALTER TABLE cockatrice_users ADD INDEX idx_privlevel (`privlevel`); - -UPDATE cockatrice_schema_version SET version=31 WHERE version=30; diff --git a/servatrice/migrations/servatrice_0031_to_0032.sql b/servatrice/migrations/servatrice_0031_to_0032.sql deleted file mode 100644 index dd3a1334c..000000000 --- a/servatrice/migrations/servatrice_0031_to_0032.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Servatrice db migration from version 31 to version 32 - -ALTER TABLE cockatrice_users ADD INDEX `idx_clientid` (`clientid`); -ALTER TABLE cockatrice_sessions ADD INDEX `idx_clientid` (`clientid`); -ALTER TABLE cockatrice_sessions ADD INDEX `idx_ip_address` (`ip_address`); -ALTER TABLE cockatrice_bans ADD INDEX `idx_user_name` (`user_name`); -ALTER TABLE cockatrice_warnings ADD INDEX `idx_time_of` (`time_of`); -ALTER TABLE cockatrice_warnings ADD INDEX `idx_user_name` (`user_name`); -ALTER TABLE cockatrice_log ADD INDEX `idx_log_time` (`log_time`); - -UPDATE cockatrice_schema_version SET version=32 WHERE version=31; diff --git a/servatrice/migrations/servatrice_0032_to_0033.sql b/servatrice/migrations/servatrice_0032_to_0033.sql deleted file mode 100644 index 5706c01f8..000000000 --- a/servatrice/migrations/servatrice_0032_to_0033.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Servatrice db migration from version 32 to version 33 - -ALTER TABLE cockatrice_user_analytics ADD INDEX `idx_last_login` (`last_login`); - -UPDATE cockatrice_schema_version SET version=33 WHERE version=32; diff --git a/servatrice/migrations/servatrice_0033_to_0034.sql b/servatrice/migrations/servatrice_0033_to_0034.sql deleted file mode 100644 index 19c33c2dc..000000000 --- a/servatrice/migrations/servatrice_0033_to_0034.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Servatrice db migration from version 33 to version 34 - -ALTER TABLE cockatrice_users ADD COLUMN leftPawnColorOverride varchar(255); -ALTER TABLE cockatrice_users ADD COLUMN rightPawnColorOverride varchar(255); - -ALTER TABLE cockatrice_users ADD INDEX `idx_pawnColorOverrides` (`leftPawnColorOverride`, `rightPawnColorOverride`); - -UPDATE cockatrice_schema_version SET version=34 WHERE version=33; diff --git a/servatrice/resources/appicon.icns b/servatrice/resources/appicon.icns index 1f3b5c207..e2097028e 100644 Binary files a/servatrice/resources/appicon.icns and b/servatrice/resources/appicon.icns differ diff --git a/servatrice/scripts/linux/db_backup_all b/servatrice/scripts/linux/db_backup_all index e97b91cc5..35aa5d411 100644 --- a/servatrice/scripts/linux/db_backup_all +++ b/servatrice/scripts/linux/db_backup_all @@ -25,6 +25,7 @@ TABLES=( "cockatrice_schema_version" "cockatrice_servermessages" "cockatrice_servers" + "cockatrice_news" "cockatrice_rooms" "cockatrice_rooms_gametypes" ) diff --git a/servatrice/scripts/linux/db_restore_all b/servatrice/scripts/linux/db_restore_all index 376a9fc00..37d1f31e6 100644 --- a/servatrice/scripts/linux/db_restore_all +++ b/servatrice/scripts/linux/db_restore_all @@ -25,6 +25,7 @@ TABLES=( "cockatrice_schema_version" "cockatrice_servermessages" "cockatrice_servers" + "cockatrice_news" "cockatrice_rooms" "cockatrice_rooms_gametypes" ) diff --git a/servatrice/scripts/linux/maint_privlevel b/servatrice/scripts/linux/maint_privlevel deleted file mode 100644 index cced056e3..000000000 --- a/servatrice/scripts/linux/maint_privlevel +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# SCHEDULE WITH CRONTAB TO RUN ON A REGULAR BASIS - -DBNAME="servatrice" #set this to the database name used -TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) -SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file -mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set privlevel = 'NONE' where privelevel != 'NONE" AND privlevelEndDate < NOW()" diff --git a/servatrice/scripts/linux/maint_replays b/servatrice/scripts/linux/maint_replays index 98eb3c4e2..bee3d0b05 100644 --- a/servatrice/scripts/linux/maint_replays +++ b/servatrice/scripts/linux/maint_replays @@ -1,7 +1,8 @@ #!/bin/bash -# SCHEDULE WITH CRONTAB DAILY +# SCHEDULE WITH CRONTAB BASED ON TIME PERIOD REPLAYS SHOULD BE SAVED UNTIL (EX: SCHEDULE ONCE A WEEK TO KEEP A WEEKS WORTH OF REPLAYS IN THE DB) + DBNAME="servatrice" #set this to the database name used TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file -mysql --defaults-file=$SQLCONFFILE -h localhost -e 'delete from servatrice.cockatrice_games where time_finished < DATE_SUB(now(), INTERVAL 8 DAY)' \ No newline at end of file +mysql --defaults-file=$SQLCONFFILE -h localhost -e 'truncate table ""$DBNAME"".""$TABLEPREFIX""_replays;truncate table ""$DBNAME"".""$TABLEPREFIX""_replays_access' diff --git a/servatrice/scripts/linux/setup_addfirstadmin b/servatrice/scripts/linux/setup_addfirstadmin index 9ea2511c9..abbaddcf7 100644 --- a/servatrice/scripts/linux/setup_addfirstadmin +++ b/servatrice/scripts/linux/setup_addfirstadmin @@ -5,4 +5,4 @@ DBNAME="servatrice" #set this to the database name used TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _) SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file -mysql --defaults-file=$SQLCONFFILE -h localhost -e "insert into ""$DBNAME"".""$TABLEPREFIX""_users ((admin,name,password_sha512,active,realname,email,country,avatar_bmp,registrationDate,clientID,adminnotes,privlevelStartDate,privlevelEndDate) values (1,'servatrice','jbB4kSWDmjaVzMNdU13n73SpdBCJTCJ/JYm5ZBZvfxlzbISbXir+e/aSvMz86KzOoaBfidxO0s6GVd8t00qC0TNPl+udHfECaF7MsA==',1,'servatrice','servatrice@localhost','us','null.bmp','1970-01-01 10:00:00','','','1970-01-01 10:00:00','9999-01-01 10:00:00'); +mysql --defaults-file=$SQLCONFFILE -h localhost -e "insert into ""$DBNAME"".""$TABLEPREFIX""_users (admin,name,password_sha512,active) values (1,'servatrice','jbB4kSWDmjaVzMNdU13n73SpdBCJTCJ/JYm5ZBZvfxlzbISbXir+e/aSvMz86KzOoaBfidxO0s6GVd8t00qC0TNPl+udHfECaF7MsA==',1);" diff --git a/servatrice/scripts/mk_pypb.sh b/servatrice/scripts/mk_pypb.sh new file mode 100755 index 000000000..e11d237d2 --- /dev/null +++ b/servatrice/scripts/mk_pypb.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +SRC_DIR=../../common/pb/ +DST_DIR=./pypb + +rm -rf "$DST_DIR" +mkdir -p "$DST_DIR" +protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/*.proto +touch "$DST_DIR/__init__.py" + diff --git a/servatrice/scripts/register.py b/servatrice/scripts/register.py index d33db2e21..427c9e72b 100755 --- a/servatrice/scripts/register.py +++ b/servatrice/scripts/register.py @@ -9,7 +9,7 @@ from pypb.event_server_identification_pb2 import Event_ServerIdentification as S from pypb.response_pb2 import Response HOST = "localhost" -PORT = 4748 +PORT = 4747 CMD_ID = 1 diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index fac743c39..06d004977 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -3,8 +3,7 @@ ; This is the main configuration file for Servatrice; while using a configuration is not mandatory, ; you may want to customize some aspects of your servatrice instance, like its name, port or the way ; users can authenticate to the server. -; -; Can be passed to servatrice with --config /path/to/servatrice.ini + [server] @@ -15,32 +14,14 @@ name="My Cockatrice server" ; must have a different id; the default id is 1 id=1 -; The IP address servatrice will listen on for clients; defaults to "any" to listen on all the interfaces, -; a specific IPv4/v6 addresses can be used, eg for localhost-only 127.0.0.1 for IPv4 or ::1 for IPv6. - -host=any - -; The TCP port number Servatrice will listen on for clients; default is 4747; -; Will be removed in the future, use websocket connection instead +; The TCP port number servatrice will listen on for clients; default is 4747 port=4747 ; Servatrice can scale up to serve big number of users using more than one parallel thread of execution; ; If your server is hosting a lot of players and they frequently report of being unable to login or ; long delays (lag), you may want to try increasing this value; default is 1. -; Set to 0 to disable the tcp server. number_pools=1 -; Servatrice can listen for clients on websockets, too. Multiple connection pools are available but -; unfortunately, due to a Qt limitation, they must run in the same execution thread. -; Set to 0 to disable the websocket server. -websocket_number_pools=1 - -; The IP address servatrice will listen on for websockets clients; defaults to "any" -websocket_host=any - -; The TCP port number servatrice will listen on for websockets clients; default is 4748 -websocket_port=4748 - ; When database is enabled, servatrice writes the server status in the "update" database table; this ; setting defines every how many milliseconds servatrice will update its status; default is 15000 (15 secs) statusupdate=15000 @@ -50,13 +31,11 @@ writelog=1 ; Choose a name for the log file, if enabled; you can specify an absolute path or a path relative to ; the servatrice executable; the default file name is server.log (in the same path as servatrice) -; Note: When running servatrice under windows you will need to use double backslashes between folder locations -; [ex: C:\\Temp\\server.log ] logfile=server.log -; You may want to log only certain messages in the logfile. The default log level is extremely verbose. -; This setting should contain a comma-separated list of strings that will be selectively logged. -; All other lines will be excluded from the log. Default is empty; example: "Registration,_Login,foobar" +; You may want to silence some commonly recurring messages in the logfile. This setting can contain a +; comma-separed list of words; if any message that is about to be logged contains at least one of these words, +; it won't be logged. Default is empty; example: "kittens,ponies,faires" logfilters="" ; Set the time interval in seconds that servatrice will use to communicate with each connected client @@ -68,23 +47,19 @@ clientkeepalive=1 max_player_inactivity_time=15 ; More modern clients generate client IDs based on specific client side information. Enable this option to -; require that clients report the client ID in order to log into the server. Default is false +' require that clients report the client ID in order to log into the server. Default is false requireclientid=false ; You can limit the types of clients that connect to the server by requiring different features be available ; on the client. This setting can contain a comma-seperated list of features. if any of the features ; listed in this line are not available on the client the client will be denied access to the server upon -; attempting to log in. Example: "client_id,client_ver,websocket" +; attempting to log in. Example: "client_id,client_ver" requiredfeatures="" ; You can define custom warnings that users are sent when the moderation staff uses the right client warn user ; menu option. This list is comma seperated that each item will appear in the drop down list for staff members ; to choose from. Example: "Flaming,Foul Language" -officialwarnings="Flaming,Spamming,Causing Drama,Abusive Language" - -; Maximum time in seconds a player can stay connected but idle. Default is 3600 (0 = disabled) -; Clients will be notified at the 90% time period of pending disconnection if they do not take action. -idleclienttimeout=3600 +officialwarnings="Flamming,Spamming,Causing Drama,Abusive Language" [authentication] @@ -124,100 +99,19 @@ allowedpunctuation=_.- ; If a username can begin with punctuation defined in allowedpunctuation allowpunctuationprefix=false -; Disallow usernames containing these words. This list is comma seperated, e.g. -; "admin,user,name" -disallowedwords="admin" - -; Overwrite the words shown to the user when they enter a wrong username, -; use \n to start a new line. Neither the real wordlist nor the disallowed -; expressions will be sent to the user if this is set. -; In the old versions of the client this list will be prefaced with -; "can not contain any of the following words:" -; example: -;displaydisallowedwords="no attempts at impersonating staff\nno unparliamentary language\nno references to controversial figures\nstaff reserves the right to remove accounts deemed inappropriate" -; Setting it to nothing will simply hide the list: -;displaydisallowedwords= - -; Disallow usernames matching these regular expressions. This list is comma -; separated, e.g. "\\w+\\d+,\\d{2}user", hence you cannot use commas in your -; expressions. Backslashes must be escaped, so `\w+\d+` becomes `\\w+\\d+`. -; WARNING: Complex expressions can be harmful to performance. Please make sure -; your expressions are considered well formed. See this page for info: -; http://www.regular-expressions.info/catastrophic.html -disallowedregexp="" - -; Define minimum password length -; Default 6. -minpasswordlength = 6 - [registration] ; Servatrice can process registration requests to add new users on the fly. ; Enable this feature? Default false. ;enabled=false -; Require users to provide an email address in order to register. Default true. +; Require users to provide an email address in order to register. Newly registered users will receive an +; activation token by email, and will be required to input back this token on cockatrice at the first login +; to get their account activated. Default true. ;requireemail=true -; Require email activation. Newly registered users will receive an activation token by email, -; and will be required to input back this token on cockatrice at the first login to get their -; account activated. Default true. -;requireemailactivation=true - -; Set this number to the maximum number of accounts any one user can use to create new accounts -; using the same email address. 0 = Unlimited number of accounts (default). -;maxaccountsperemail=0 - -; You can prevent users from using certain mail domains for registration. This setting contains a -; comma-seperated list of email provider domains that you would like to prevent users from using -; during registration. Comparison's are implicit, so placing an entry such as mail.com will also -; prevent users from registering accounts with providers such as gmail.com and hotmail.com -; Example: "10minutemail.com,gmail.com" -;emailproviderblacklist="" - -; You can require users to only use certain email domains for registration. This setting is a -; comma-separated list of email provider domains that you have explicitly audited and require -; the use of in order to create an account. Comparison's are explicit, so you must specify the -; domain in completion, such as gmail.com and hotmail.com. Email whitelist is checked before -; Email blacklist is checked, so an email cannot be in both setting configurations. -; Example: "gmail.com,hotmail.com,icloud.com" -;emailproviderwhitelist="" - -[forgotpassword] - -; Servatrice can process reset password requests allowing users to reset their account -; passwords in the event they forget it. Should this feature be enabled? Default: false. -; enable=false - -; Reset password request should not be allowed to stay valid forever. This settings -; informs servatrice how long a players reset password reset token is valid for (in minutes). -; Default: 60 -; tokenlife=60 - -; Servatrice can challenge users that are making reset password requests to answer -; questions in regards to their account to help validate they are the true owner of the account. -; Should this feature be enabled? Default: false -; enablechallenge=false - -; Email subject for the reset password emails -; subject="Cockatrice reset password token" - -; Reset password email body. You can use these tags here: %username %token -; They will be substituted with the actual values in the email -; -; body="Hi %username,\r\nthanks for reaching out to us with your password reset request for our Cockatrice server.\r\nHere's your unique token in order to reset your account password in the app:\r\n\r\n%token\r\n\r\nHappy gaming!" - - [smtp] -; Enable the internal smtp client to send registration emails. If you would like to -; use some other method to send email activation tokens set this value to false. Otherwise -; setting it to true (default) the server will send canned generated emails containing -; activation tokens for you during update intervals. Setting this to false will require -; you to either manually activate user accounts or manually send users the activation token -; by whatever means. -enableinternalsmtpclient=true - ; Connectin type: currently supported method are "tcp" and "ssl"; tls is autodetected if available connection=tcp @@ -293,10 +187,6 @@ roomlist\1\description="Play anything here." ; Default is none. roomlist\1\permissionlevel=none -; Rooms can restrict the permission level of users that can join, Currnetly supported options are none, privileged, vip, and donator. -; Default is none. -roomlist\1\privilegelevel=none - ; Wether to make users autojoin this room when connected to the server roomlist\1\autojoin=true @@ -327,11 +217,6 @@ max_game_inactivity_time=120 ; the database. Default value is true. store_replays=true -; Allow users to create a new game and join it as a judge. The host will be able to execute any action on -; the cards of every player. This is needed in order to support some games (eg. Werewolf). -; Default off to prevent abuse on servers that are mostly running other games. -allow_create_as_judge=false - [security] ; You may want to restrict the number of users that can connect to your server at any given time. enable_max_user_limit=false @@ -339,12 +224,6 @@ enable_max_user_limit=false ; Maximum number of users that can connect to the server, default is 500. max_users_total=500 -; Maximum number of users that can connect to the server using a tcp connection, default is 500. -max_users_tcp=500 - -; Maximum number of users that can connect to the server using a websocket connection, default is 500. -max_users_websocket=500 - ; Maximum number of users that can connect from the same IP address; useful to avoid bots, default is 4 max_users_per_address=4 @@ -363,7 +242,7 @@ max_message_size_per_interval=1000 ; Maximum number of messages in an interval before new messages gets dropped; default is 10 max_message_count_per_interval=10 -; Maximum number of games a single user can create; default is 5; set to -1 to disable; 0 disallows game creation +; Maximum number of games a single user can create; default is 5 max_games_per_user=5 ; Servatrice can avoid users from flooding games with large number of game commands in an interval of time. @@ -374,10 +253,6 @@ command_counting_interval=10 max_command_count_per_interval=20 [logging] -; Admin/Moderators can query the stored logs for information when looking up reports by various players. This -; option can allow or disallow them from doing so. -; !!NOTE!! Enabling this feature puts a very high CPU and DISK load on the server, enable with caution. -enablelogquery=false ; Servatrice can log user messages to the database table cockatrice_log. ; These messages can come from different sources; each source can be enabled separately. @@ -394,22 +269,6 @@ log_user_msg_chat=false ; Log user messages coming from other servers in the network log_user_msg_isl=false -[audit] - -; Servatrice can record certain actions being performed in the database for server operators to better understand -; if some one may be abusing application functionality. Enabling auditing will allow servatrice to record any -; of the below enabled audit functionality to be recorded. -; Default: true -enable_audit=true - -; Servatrice can record when users attempt a new account registration. Should we enable auditing for this action? -; Default: true -enable_registration_audit=true - -; Servatrice can record when a users attempts to reset the account password. Should we enable auditing for this action? -; Default: true -enable_forgotpassword_audit=true - ; EXPERIMENTAL - NOT WORKING YET ; The following settings are relative to the server network functionality, that is not yet complete. @@ -422,7 +281,7 @@ enable_forgotpassword_audit=true ; "servers" table of the database. Default is 0 (disabled) active=0 -; The TCP port number Servatrice will listen on for other servers; default is 14747 +; The TCP port number servatrice will listen on for other servers; default is 14747 port=14747 ; Server-to-server communication needs a valid certificate in PEM format. Enter its filename in this setting diff --git a/servatrice/servatrice.sql b/servatrice/servatrice.sql index fa644dbc0..7a35b6953 100644 --- a/servatrice/servatrice.sql +++ b/servatrice/servatrice.sql @@ -18,41 +18,9 @@ SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` ( `version` int(7) unsigned NOT NULL, PRIMARY KEY (`version`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -INSERT INTO cockatrice_schema_version VALUES(34); - --- users and user data tables -CREATE TABLE IF NOT EXISTS `cockatrice_users` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `admin` tinyint(1) NOT NULL, - `name` varchar(35) NOT NULL, - `realname` varchar(255) NOT NULL, - `password_sha512` char(120) NOT NULL, - `email` varchar(255) NOT NULL, - `country` char(2) NOT NULL, - `avatar_bmp` mediumblob NOT NULL, - `registrationDate` datetime NOT NULL, - `active` tinyint(1) NOT NULL, - `token` binary(16), - `clientid` varchar(15) NOT NULL, - `adminnotes` mediumtext NOT NULL, - `privlevel` enum("NONE","VIP","DONATOR") NOT NULL, - `privlevelStartDate` datetime NOT NULL, - `privlevelEndDate` datetime NOT NULL, - `passwordLastChangedDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `leftPawnColorOverride` varchar(255), - `rightPawnColorOverride` varchar(255), - PRIMARY KEY (`id`), - UNIQUE KEY `name` (`name`), - KEY `token` (`token`), - KEY `email` (`email`), - INDEX `idx_admin` (`admin`), - INDEX `idx_active` (`active`), - INDEX `idx_privlevel` (`privlevel`), - INDEX `idx_clientid` (`clientid`), - INDEX `idx_pawnColorOverrides` (`leftPawnColorOverride`, `rightPawnColorOverride`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +INSERT INTO cockatrice_schema_version VALUES(12); CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` ( `id` int(7) unsigned zerofill NOT NULL auto_increment, @@ -62,9 +30,8 @@ CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` ( `upload_time` datetime NOT NULL, `content` text NOT NULL, PRIMARY KEY (`id`), - KEY `FolderPlusUser` (`id_folder`,`id_user`), - FOREIGN KEY(`id_user`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + KEY `FolderPlusUser` (`id_folder`,`id_user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_decklist_folders` ( `id` int(7) unsigned zerofill NOT NULL auto_increment, @@ -72,141 +39,94 @@ CREATE TABLE IF NOT EXISTS `cockatrice_decklist_folders` ( `id_user` int(7) unsigned NULL, `name` varchar(30) NOT NULL, PRIMARY KEY (`id`), - KEY `ParentPlusUser` (`id_parent`,`id_user`), - FOREIGN KEY(`id_user`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + KEY `ParentPlusUser` (`id_parent`,`id_user`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -CREATE TABLE IF NOT EXISTS `cockatrice_ignorelist` ( - `id_user1` int(7) unsigned NOT NULL, - `id_user2` int(7) unsigned NOT NULL, - UNIQUE KEY `key` (`id_user1`, `id_user2`), - FOREIGN KEY(`id_user1`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_user2`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_buddylist` ( - `id_user1` int(7) unsigned NOT NULL, - `id_user2` int(7) unsigned NOT NULL, - UNIQUE KEY `key` (`id_user1`, `id_user2`), - FOREIGN KEY(`id_user1`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_user2`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- rooms -CREATE TABLE IF NOT EXISTS `cockatrice_rooms` ( - `id` int(7) unsigned NOT NULL auto_increment, - `name` varchar(50) NOT NULL, - `descr` varchar(255) NOT NULL, - `permissionlevel` enum('NONE','REGISTERED','MODERATOR','ADMINISTRATOR') NOT NULL, - `privlevel` enum('NONE','PRIVILEGED','VIP','DONATOR') NOT NULL, - `auto_join` tinyint(1) default 0, - `join_message` varchar(255) NOT NULL, - `chat_history_size` int(4) NOT NULL, - `id_server` tinyint(3) NOT NULL DEFAULT 1, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_rooms_gametypes` ( - `id_room` int(7) unsigned NOT NULL, - `name` varchar(50) NOT NULL, - `id_server` tinyint(3) NOT NULL DEFAULT 1, - FOREIGN KEY(`id_room`) REFERENCES `cockatrice_rooms`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- games CREATE TABLE IF NOT EXISTS `cockatrice_games` ( `room_name` varchar(255) NOT NULL, `id` int(7) unsigned NOT NULL auto_increment, `descr` varchar(50) default NULL, - `creator_name` varchar(35) NOT NULL, + `creator_name` varchar(255) NOT NULL, `password` tinyint(1) NOT NULL, `game_types` varchar(255) NOT NULL, `player_count` tinyint(3) NOT NULL, `time_started` datetime default NULL, `time_finished` datetime default NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_games_players` ( `id_game` int(7) unsigned zerofill NOT NULL, - `player_name` varchar(35) NOT NULL, - FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + `player_name` varchar(255) NOT NULL, + KEY `id_game` (`id_game`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; --- Note: an empty row with id_game = NULL is created when the game is created, --- and then updated when the game ends with the full replay data. -CREATE TABLE IF NOT EXISTS `cockatrice_replays` ( - `id` int(7) NOT NULL AUTO_INCREMENT, - `id_game` int(7) unsigned NULL, - `duration` int(7) NOT NULL, - `replay` mediumblob NOT NULL, - PRIMARY KEY (`id`), - FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS `cockatrice_news` ( + `id` int(7) unsigned zerofill NOT NULL auto_increment, + `id_user` int(7) unsigned zerofill NOT NULL, + `news_date` datetime NOT NULL, + `subject` varchar(255) NOT NULL, + `content` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -CREATE TABLE IF NOT EXISTS `cockatrice_replays_access` ( - `id_game` int(7) unsigned NOT NULL, - `id_player` int(7) unsigned NOT NULL, - `replay_name` varchar(255) NOT NULL, - `do_not_hide` tinyint(1) NOT NULL, - KEY `id_player` (`id_player`), - FOREIGN KEY(`id_game`) REFERENCES `cockatrice_games`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, - FOREIGN KEY(`id_player`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - --- server administration - --- Note: unused table -CREATE TABLE IF NOT EXISTS `cockatrice_servers` ( - `id` mediumint(8) unsigned NOT NULL, - `ssl_cert` text NOT NULL, - `hostname` varchar(255) NOT NULL, - `address` varchar(255) NOT NULL, - `game_port` mediumint(8) unsigned NOT NULL, - `control_port` mediumint(9) NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS `cockatrice_users` ( + `id` int(7) unsigned zerofill NOT NULL auto_increment, + `admin` tinyint(1) NOT NULL, + `name` varchar(35) NOT NULL, + `realname` varchar(255) NOT NULL, + `gender` char(1) NOT NULL, + `password_sha512` char(120) NOT NULL, + `email` varchar(255) NOT NULL, + `country` char(2) NOT NULL, + `avatar_bmp` blob NOT NULL, + `registrationDate` datetime NOT NULL, + `active` tinyint(1) NOT NULL, + `token` binary(16), + `clientid` varchar(15) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `token` (`token`), + KEY `email` (`email`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_uptime` ( `id_server` tinyint(3) NOT NULL, `timest` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `uptime` int(11) NOT NULL, `users_count` int(11) NOT NULL, - `mods_count` int(11) NOT NULL DEFAULT 0, - `mods_list` TEXT, `games_count` int(11) NOT NULL, `rx_bytes` int(11) NOT NULL, `tx_bytes` int(11) NOT NULL, PRIMARY KEY (`timest`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_servermessages` ( - `id_server` tinyint(3) not null default 1, + `id_server` tinyint(3) not null default 0, `timest` datetime NOT NULL default '0000-00-00 00:00:00', `message` text, PRIMARY KEY (`timest`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -CREATE TABLE IF NOT EXISTS `cockatrice_sessions` ( - `id` int(9) NOT NULL AUTO_INCREMENT, - `user_name` varchar(35) NOT NULL, - `id_server` tinyint(3) NOT NULL, - `ip_address` varchar(45) NOT NULL, - `start_time` datetime NOT NULL, - `end_time` datetime DEFAULT NULL, - `clientid` varchar(15) NOT NULL, - `connection_type` ENUM('tcp', 'websocket'), - PRIMARY KEY (`id`), - KEY `username` (`user_name`), - INDEX `idx_start_time` (`start_time`), - INDEX `idx_clientid` (`clientid`), - INDEX `idx_ip_address` (`ip_address`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; +CREATE TABLE IF NOT EXISTS `cockatrice_ignorelist` ( + `id_user1` int(7) unsigned NOT NULL, + `id_user2` int(7) unsigned NOT NULL, + UNIQUE KEY `key` (`id_user1`, `id_user2`), + KEY `id_user1` (`id_user1`), + KEY `id_user2` (`id_user2`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_buddylist` ( + `id_user1` int(7) unsigned NOT NULL, + `id_user2` int(7) unsigned NOT NULL, + UNIQUE KEY `key` (`id_user1`, `id_user2`), + KEY `id_user1` (`id_user1`), + KEY `id_user2` (`id_user2`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; --- server moderation CREATE TABLE IF NOT EXISTS `cockatrice_bans` ( - `user_name` varchar(35) NOT NULL, - `ip_address` varchar(45) NOT NULL, + `user_name` varchar(255) NOT NULL, + `ip_address` varchar(255) NOT NULL, `id_admin` int(7) unsigned zerofill NOT NULL, `time_from` datetime NOT NULL, `minutes` int(6) NOT NULL, @@ -215,88 +135,100 @@ CREATE TABLE IF NOT EXISTS `cockatrice_bans` ( `clientid` varchar(15) NOT NULL, PRIMARY KEY (`user_name`,`time_from`), KEY `time_from` (`time_from`,`ip_address`), - KEY `ip_address` (`ip_address`), - INDEX `idx_user_name` (`user_name`), - FOREIGN KEY(`id_admin`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + KEY `ip_address` (`ip_address`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_warnings` ( `user_id` int(7) unsigned NOT NULL, - `user_name` varchar(35) NOT NULL, - `mod_name` varchar(35) NOT NULL, + `user_name` varchar(255) NOT NULL, + `mod_name` varchar(255) NOT NULL, `reason` text NOT NULL, `time_of` datetime NOT NULL, `clientid` varchar(15) NOT NULL, - PRIMARY KEY (`user_id`,`time_of`), - INDEX `idx_time_of` (`time_of`), - INDEX `idx_user_name` (`user_name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + PRIMARY KEY (`user_id`,`time_of`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_sessions` ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `user_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `id_server` tinyint(3) NOT NULL, + `ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL, + `start_time` datetime NOT NULL, + `end_time` datetime DEFAULT NULL, + `clientid` varchar(15) NOT NULL, + PRIMARY KEY (`id`), + KEY `username` (`user_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE IF NOT EXISTS `cockatrice_servers` ( + `id` mediumint(8) unsigned NOT NULL, + `ssl_cert` text COLLATE utf8_unicode_ci NOT NULL, + `hostname` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `address` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `game_port` mediumint(8) unsigned NOT NULL, + `control_port` mediumint(9) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_replays` ( + `id` int(7) NOT NULL AUTO_INCREMENT, + `id_game` int(7) NOT NULL, + `duration` int(7) NOT NULL, + `replay` mediumblob NOT NULL, + PRIMARY KEY (`id`), + KEY `id_game` (`id_game`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_replays_access` ( + `id_game` int(7) NOT NULL, + `id_player` int(7) NOT NULL, + `replay_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `do_not_hide` tinyint(1) NOT NULL, + KEY `id_player` (`id_player`), + KEY `id_game` (`id_game`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_rooms` ( + `id` int(7) unsigned NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + `descr` varchar(255) NOT NULL, + `permissionlevel` varchar(20) NOT NULL, + `auto_join` tinyint(1) default 0, + `join_message` varchar(255) NOT NULL, + `chat_history_size` int(4) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cockatrice_rooms_gametypes` ( + `id_room` int(7) unsigned NOT NULL, + `name` varchar(50) NOT NULL, + KEY (`id_room`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_log` ( `log_time` datetime NOT NULL, `sender_id` int(7) unsigned NULL, `sender_name` varchar(35) NOT NULL, - `sender_ip` varchar(45) NOT NULL, + `sender_ip` varchar(255) NOT NULL, `log_message` text NOT NULL, `target_type` ENUM('room', 'game', 'chat'), `target_id` int(7) NULL, `target_name` varchar(50) NOT NULL, KEY `sender_name` (`sender_name`), KEY `sender_ip` (`sender_ip`), + KEY `target_type` (`target_type`), KEY `target_id` (`target_id`), - KEY `target_name` (`target_name`), - INDEX `idx_log_time` (`log_time`), - FOREIGN KEY(`sender_id`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE - -- No FK on target_id, it can be zero -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + KEY `target_name` (`target_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_activation_emails` ( - `name` varchar(35) NOT NULL, - FOREIGN KEY(`name`) REFERENCES `cockatrice_users`(`name`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + `name` varchar(35) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `cockatrice_user_analytics` ( `id` int(7) unsigned zerofill NOT NULL, `client_ver` varchar(35) NOT NULL, `last_login` datetime NOT NULL, `notes` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - INDEX `idx_last_login` (`last_login`), - FOREIGN KEY(`id`) REFERENCES `cockatrice_users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_donations` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `username` varchar(35) DEFAULT NULL, - `email` varchar(255) DEFAULT NULL, - `payment_pre_fee` double DEFAULT NULL, - `payment_post_fee` double DEFAULT NULL, - `term_length` int(11) DEFAULT NULL, - `date` varchar(255) DEFAULT NULL, - `pp_type` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_forgot_password` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `name` varchar(35) NOT NULL, - `requestDate` datetime NOT NULL default '0000-00-00 00:00:00', - `emailed` tinyint(1) NOT NULL default 0, - PRIMARY KEY (`id`), - KEY `user_name` (`name`), - INDEX `idx_emailed` (`emailed`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; - -CREATE TABLE IF NOT EXISTS `cockatrice_audit` ( - `id` int(7) unsigned zerofill NOT NULL auto_increment, - `id_server` tinyint(3) NOT NULL, - `name` varchar(35) NOT NULL, - `ip_address` varchar(45) NOT NULL, - `clientid` varchar(15) NOT NULL, - `incidentDate` datetime NOT NULL default '0000-00-00 00:00:00', - `action` varchar(35) NOT NULL, - `results` ENUM('fail', 'success') NOT NULL DEFAULT 'fail', - `details` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - KEY `user_name` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/servatrice/src/email_parser.cpp b/servatrice/src/email_parser.cpp deleted file mode 100644 index d8c30d852..000000000 --- a/servatrice/src/email_parser.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "email_parser.h" - -#include -#include - -QPair EmailParser::parseEmailAddress(const QString &dirtyEmailAddress) -{ - // https://www.regular-expressions.info/email.html - static const QRegularExpression emailRegex(R"(^([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,})$)", - QRegularExpression::CaseInsensitiveOption); - const auto match = emailRegex.match(dirtyEmailAddress); - - if (dirtyEmailAddress.isEmpty() || !match.hasMatch()) { - return {}; - } - - QString capturedEmailUser = match.captured(1); - QString capturedEmailAddressDomain = match.captured(2); - - // Replace googlemail.com with gmail.com, as is standard nowadays - // https://www.gmass.co/blog/domains-gmail-com-googlemail-com-and-google-com/ - if (capturedEmailAddressDomain.toLower() == "googlemail.com") { - capturedEmailAddressDomain = "gmail.com"; - } - - // Trim out dots and pluses from Google/Gmail domains - if (capturedEmailAddressDomain.toLower() == "gmail.com") { - // Remove all content after the first plus sign (as unnecessary with gmail) - // https://gmail.googleblog.com/2008/03/2-hidden-ways-to-get-more-from-your.html - const auto firstPlusSign = capturedEmailUser.indexOf("+"); - if (firstPlusSign != -1) { - capturedEmailUser = capturedEmailUser.left(firstPlusSign); - } - - // Remove all periods (as unnecessary with gmail) - // https://gmail.googleblog.com/2008/03/2-hidden-ways-to-get-more-from-your.html - capturedEmailUser.replace(".", ""); - } - // Trim out minuses from Yahoo domains - else if (capturedEmailAddressDomain.toLower() == "yahoo.com") { - const auto firstMinusSign = capturedEmailUser.indexOf("-"); - if (firstMinusSign != -1) { - capturedEmailUser = capturedEmailUser.left(firstMinusSign); - } - } - - return {capturedEmailUser, capturedEmailAddressDomain}; -} - -QString EmailParser::getParsedEmailAddress(const QString &dirtyEmailAddress) -{ - const auto parsedEmailAddress = EmailParser::parseEmailAddress(dirtyEmailAddress); - return EmailParser::getParsedEmailAddress(parsedEmailAddress); -} - -QString EmailParser::getParsedEmailAddress(const QPair &emailAddressIntermediate) -{ - const auto emailUser = emailAddressIntermediate.first; - const auto emailDomain = emailAddressIntermediate.second; - return emailUser + "@" + emailDomain; -} \ No newline at end of file diff --git a/servatrice/src/email_parser.h b/servatrice/src/email_parser.h deleted file mode 100644 index 4cb6ab059..000000000 --- a/servatrice/src/email_parser.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef COCKATRICE_EMAILPARSER_H -#define COCKATRICE_EMAILPARSER_H - -#include -#include - -class EmailParser -{ -public: - static QPair parseEmailAddress(const QString &dirtyEmailAddress); - static QString getParsedEmailAddress(const QString &dirtyEmailAddress); - static QString getParsedEmailAddress(const QPair &emailAddressIntermediate); -}; - -#endif // COCKATRICE_EMAILPARSER_H diff --git a/servatrice/src/isl_interface.cpp b/servatrice/src/isl_interface.cpp index 692a0fdba..2a9ea41b2 100644 --- a/servatrice/src/isl_interface.cpp +++ b/servatrice/src/isl_interface.cpp @@ -1,475 +1,415 @@ #include "isl_interface.h" - -#include "main.h" -#include "server_logger.h" - -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "server_logger.h" +#include "main.h" +#include "server_protocolhandler.h" +#include "server_room.h" -inline Q_LOGGING_CATEGORY(IslInterfaceLog, "isl_interface"); +#include "get_pb_extension.h" +#include "pb/isl_message.pb.h" +#include "pb/event_game_joined.pb.h" +#include "pb/event_server_complete_list.pb.h" +#include "pb/event_user_message.pb.h" +#include "pb/event_user_joined.pb.h" +#include "pb/event_user_left.pb.h" +#include "pb/event_join_room.pb.h" +#include "pb/event_leave_room.pb.h" +#include "pb/event_room_say.pb.h" +#include "pb/event_list_games.pb.h" +#include void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey) { - socket = new QSslSocket(this); - socket->setLocalCertificate(cert); - socket->setPrivateKey(privateKey); - - connect(socket, SIGNAL(readyRead()), this, SLOT(readClient()), Qt::QueuedConnection); - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(catchSocketError(QAbstractSocket::SocketError))); - connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); + socket = new QSslSocket(this); + socket->setLocalCertificate(cert); + socket->setPrivateKey(privateKey); + + connect(socket, SIGNAL(readyRead()), this, SLOT(readClient()), Qt::QueuedConnection); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); + connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); } -IslInterface::IslInterface(int _socketDescriptor, - const QSslCertificate &cert, - const QSslKey &privateKey, - Servatrice *_server) - : QObject(), socketDescriptor(_socketDescriptor), server(_server), messageInProgress(false) +IslInterface::IslInterface(int _socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) + : QObject(), socketDescriptor(_socketDescriptor), server(_server), messageInProgress(false) { - sharedCtor(cert, privateKey); + sharedCtor(cert, privateKey); } -IslInterface::IslInterface(int _serverId, - const QString &_peerHostName, - const QString &_peerAddress, - int _peerPort, - const QSslCertificate &_peerCert, - const QSslCertificate &cert, - const QSslKey &privateKey, - Servatrice *_server) - : QObject(), serverId(_serverId), peerHostName(_peerHostName), peerAddress(_peerAddress), peerPort(_peerPort), - peerCert(_peerCert), server(_server), messageInProgress(false) +IslInterface::IslInterface(int _serverId, const QString &_peerHostName, const QString &_peerAddress, int _peerPort, const QSslCertificate &_peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server) + : QObject(), serverId(_serverId), peerHostName(_peerHostName), peerAddress(_peerAddress), peerPort(_peerPort), peerCert(_peerCert), server(_server), messageInProgress(false) { - sharedCtor(cert, privateKey); + sharedCtor(cert, privateKey); } IslInterface::~IslInterface() { - logger->logMessage("[ISL] session ended", this); - - flushOutputBuffer(); - - // As these signals are connected with Qt::QueuedConnection implicitly, - // we don't need to worry about them modifying the lists while we're iterating. - - server->roomsLock.lockForRead(); - QMapIterator roomIterator(server->getRooms()); - while (roomIterator.hasNext()) { - Server_Room *room = roomIterator.next().value(); - room->usersLock.lockForRead(); - QMapIterator roomUsers(room->getExternalUsers()); - while (roomUsers.hasNext()) { - roomUsers.next(); - if (roomUsers.value().getUserInfo()->server_id() == serverId) - emit externalRoomUserLeft(room->getId(), roomUsers.key()); - } - room->usersLock.unlock(); - } - server->roomsLock.unlock(); - - server->clientsLock.lockForRead(); - QMapIterator extUsers(server->getExternalUsers()); - while (extUsers.hasNext()) { - extUsers.next(); - if (extUsers.value()->getUserInfo()->server_id() == serverId) - emit externalUserLeft(extUsers.key()); - } - server->clientsLock.unlock(); + logger->logMessage("[ISL] session ended", this); + + flushOutputBuffer(); + + // As these signals are connected with Qt::QueuedConnection implicitly, + // we don't need to worry about them modifying the lists while we're iterating. + + server->roomsLock.lockForRead(); + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + room->usersLock.lockForRead(); + QMapIterator roomUsers(room->getExternalUsers()); + while (roomUsers.hasNext()) { + roomUsers.next(); + if (roomUsers.value().getUserInfo()->server_id() == serverId) + emit externalRoomUserLeft(room->getId(), roomUsers.key()); + } + room->usersLock.unlock(); + } + server->roomsLock.unlock(); + + server->clientsLock.lockForRead(); + QMapIterator extUsers(server->getExternalUsers()); + while (extUsers.hasNext()) { + extUsers.next(); + if (extUsers.value()->getUserInfo()->server_id() == serverId) + emit externalUserLeft(extUsers.key()); + } + server->clientsLock.unlock(); } void IslInterface::initServer() { - socket->setSocketDescriptor(socketDescriptor); - - logger->logMessage(QString("[ISL] incoming connection: %1").arg(socket->peerAddress().toString())); - - QList serverList = server->getServerList(); - int listIndex = -1; - for (int i = 0; i < serverList.size(); ++i) - if (serverList[i].address == socket->peerAddress()) { - listIndex = i; - break; - } - if (listIndex == -1) { - logger->logMessage( - QString("[ISL] address %1 unknown, terminating connection").arg(socket->peerAddress().toString())); - deleteLater(); - return; - } - - socket->startServerEncryption(); - if (!socket->waitForEncrypted(5000)) { - QList sslErrors(socket->sslHandshakeErrors()); - if (sslErrors.isEmpty()) { - qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection"; - } else { - qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors; - } - deleteLater(); - return; - } - - if (serverList[listIndex].cert == socket->peerCertificate()) - logger->logMessage(QString("[ISL] Peer authenticated as " + serverList[listIndex].hostname)); - else { - logger->logMessage(QString("[ISL] Authentication failed, terminating connection")); - deleteLater(); - return; - } - serverId = serverList[listIndex].id; - - Event_ServerCompleteList event; - event.set_server_id(server->getServerID()); - - server->clientsLock.lockForRead(); - QMapIterator userIterator(server->getUsers()); - while (userIterator.hasNext()) - event.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(true, true)); - server->clientsLock.unlock(); - - server->roomsLock.lockForRead(); - QMapIterator roomIterator(server->getRooms()); - while (roomIterator.hasNext()) { - Server_Room *room = roomIterator.next().value(); - room->usersLock.lockForRead(); - room->gamesLock.lockForRead(); - room->getInfo(*event.add_room_list(), true, true, false); - } - - IslMessage message; - message.set_message_type(IslMessage::SESSION_EVENT); - SessionEvent *sessionEvent = message.mutable_session_event(); - sessionEvent->GetReflection() - ->MutableMessage(sessionEvent, event.GetDescriptor()->FindExtensionByName("ext")) - ->CopyFrom(event); - - server->islLock.lockForWrite(); - if (server->islConnectionExists(serverId)) { - qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection"; - deleteLater(); - } else { - transmitMessage(message); - server->addIslInterface(serverId, this); - } - server->islLock.unlock(); - - roomIterator.toFront(); - while (roomIterator.hasNext()) { - roomIterator.next(); - roomIterator.value()->gamesLock.unlock(); - roomIterator.value()->usersLock.unlock(); - } - server->roomsLock.unlock(); + socket->setSocketDescriptor(socketDescriptor); + + logger->logMessage(QString("[ISL] incoming connection: %1").arg(socket->peerAddress().toString())); + + QList serverList = server->getServerList(); + int listIndex = -1; + for (int i = 0; i < serverList.size(); ++i) + if (serverList[i].address == socket->peerAddress()) { + listIndex = i; + break; + } + if (listIndex == -1) { + logger->logMessage(QString("[ISL] address %1 unknown, terminating connection").arg(socket->peerAddress().toString())); + deleteLater(); + return; + } + + socket->startServerEncryption(); + if (!socket->waitForEncrypted(5000)) { + QList sslErrors(socket->sslErrors()); + if (sslErrors.isEmpty()) + qDebug() << "[ISL] SSL handshake timeout, terminating connection"; + else + qDebug() << "[ISL] SSL errors:" << sslErrors; + deleteLater(); + return; + } + + if (serverList[listIndex].cert == socket->peerCertificate()) + logger->logMessage(QString("[ISL] Peer authenticated as " + serverList[listIndex].hostname)); + else { + logger->logMessage(QString("[ISL] Authentication failed, terminating connection")); + deleteLater(); + return; + } + serverId = serverList[listIndex].id; + + Event_ServerCompleteList event; + event.set_server_id(server->getServerId()); + + server->clientsLock.lockForRead(); + QMapIterator userIterator(server->getUsers()); + while (userIterator.hasNext()) + event.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(true, true)); + server->clientsLock.unlock(); + + server->roomsLock.lockForRead(); + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + room->usersLock.lockForRead(); + room->gamesLock.lockForRead(); + room->getInfo(*event.add_room_list(), true, true, false); + } + + IslMessage message; + message.set_message_type(IslMessage::SESSION_EVENT); + SessionEvent *sessionEvent = message.mutable_session_event(); + sessionEvent->GetReflection()->MutableMessage(sessionEvent, event.GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(event); + + server->islLock.lockForWrite(); + if (server->islConnectionExists(serverId)) { + qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + deleteLater(); + } else { + transmitMessage(message); + server->addIslInterface(serverId, this); + } + server->islLock.unlock(); + + roomIterator.toFront(); + while (roomIterator.hasNext()) { + roomIterator.next(); + roomIterator.value()->gamesLock.unlock(); + roomIterator.value()->usersLock.unlock(); + } + server->roomsLock.unlock(); } void IslInterface::initClient() { - QList expectedErrors; - expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); - socket->ignoreSslErrors(expectedErrors); + QList expectedErrors; + expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert)); + socket->ignoreSslErrors(expectedErrors); + + qDebug() << "[ISL] Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort; - qCDebug(IslInterfaceLog) << "Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort; - - socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName); - if (!socket->waitForConnected(5000)) { - qCDebug(IslInterfaceLog) << "Socket error:" << socket->errorString(); - deleteLater(); - return; - } - if (!socket->waitForEncrypted(5000)) { - QList sslErrors(socket->sslHandshakeErrors()); - if (sslErrors.isEmpty()) { - qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection"; - } else { - qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors; - } - deleteLater(); - return; - } - - server->islLock.lockForWrite(); - if (server->islConnectionExists(serverId)) { - qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection"; - deleteLater(); - return; - } - - server->addIslInterface(serverId, this); - server->islLock.unlock(); + socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName); + if (!socket->waitForConnected(5000)) { + qDebug() << "[ISL] Socket error:" << socket->errorString(); + deleteLater(); + return; + } + if (!socket->waitForEncrypted(5000)) { + QList sslErrors(socket->sslErrors()); + if (sslErrors.isEmpty()) + qDebug() << "[ISL] SSL handshake timeout, terminating connection"; + else + qDebug() << "[ISL] SSL errors:" << sslErrors; + deleteLater(); + return; + } + + server->islLock.lockForWrite(); + if (server->islConnectionExists(serverId)) { + qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection"; + deleteLater(); + return; + } + + server->addIslInterface(serverId, this); + server->islLock.unlock(); } void IslInterface::flushOutputBuffer() { - QMutexLocker locker(&outputBufferMutex); - if (outputBuffer.isEmpty()) - return; - server->incTxBytes(outputBuffer.size()); - socket->write(outputBuffer); - socket->flush(); - outputBuffer.clear(); + QMutexLocker locker(&outputBufferMutex); + if (outputBuffer.isEmpty()) + return; + server->incTxBytes(outputBuffer.size()); + socket->write(outputBuffer); + socket->flush(); + outputBuffer.clear(); } void IslInterface::readClient() { - QByteArray data = socket->readAll(); - server->incRxBytes(data.size()); - inputBuffer.append(data); - - do { - if (!messageInProgress) { - if (inputBuffer.size() >= 4) { - messageLength = (((quint32)(unsigned char)inputBuffer[0]) << 24) + - (((quint32)(unsigned char)inputBuffer[1]) << 16) + - (((quint32)(unsigned char)inputBuffer[2]) << 8) + - ((quint32)(unsigned char)inputBuffer[3]); - inputBuffer.remove(0, 4); - messageInProgress = true; - } else - return; - } - if (inputBuffer.size() < messageLength) - return; - - IslMessage newMessage; - bool ok = newMessage.ParseFromArray(inputBuffer.data(), messageLength); - inputBuffer.remove(0, messageLength); - messageInProgress = false; - - if (ok) { - processMessage(newMessage); - } else { - qCWarning(IslInterfaceLog) << "parsing error!"; - } - } while (!inputBuffer.isEmpty()); + QByteArray data = socket->readAll(); + server->incRxBytes(data.size()); + inputBuffer.append(data); + + do { + if (!messageInProgress) { + if (inputBuffer.size() >= 4) { + messageLength = (((quint32) (unsigned char) inputBuffer[0]) << 24) + + (((quint32) (unsigned char) inputBuffer[1]) << 16) + + (((quint32) (unsigned char) inputBuffer[2]) << 8) + + ((quint32) (unsigned char) inputBuffer[3]); + inputBuffer.remove(0, 4); + messageInProgress = true; + } else + return; + } + if (inputBuffer.size() < messageLength) + return; + + IslMessage newMessage; + newMessage.ParseFromArray(inputBuffer.data(), messageLength); + inputBuffer.remove(0, messageLength); + messageInProgress = false; + + processMessage(newMessage); + } while (!inputBuffer.isEmpty()); } void IslInterface::catchSocketError(QAbstractSocket::SocketError socketError) { - qCWarning(IslInterfaceLog) << "Socket error:" << socketError; - - server->islLock.lockForWrite(); - server->removeIslInterface(serverId); - server->islLock.unlock(); - - deleteLater(); + qDebug() << "[ISL] Socket error:" << socketError; + + server->islLock.lockForWrite(); + server->removeIslInterface(serverId); + server->islLock.unlock(); + + deleteLater(); } void IslInterface::transmitMessage(const IslMessage &item) { - QByteArray buf; -#if GOOGLE_PROTOBUF_VERSION > 3001000 - unsigned int size = static_cast(item.ByteSizeLong()); -#else - unsigned int size = static_cast(item.ByteSize()); -#endif - buf.resize(size + 4); - if (!item.SerializeToArray(buf.data() + 4, size)) { - qCWarning(IslInterfaceLog) << "transmit error!"; - return; - } - buf.data()[3] = (unsigned char)size; - buf.data()[2] = (unsigned char)(size >> 8); - buf.data()[1] = (unsigned char)(size >> 16); - buf.data()[0] = (unsigned char)(size >> 24); - - outputBufferMutex.lock(); - outputBuffer.append(buf); - outputBufferMutex.unlock(); - emit outputBufferChanged(); + QByteArray buf; + unsigned int size = item.ByteSize(); + buf.resize(size + 4); + item.SerializeToArray(buf.data() + 4, size); + buf.data()[3] = (unsigned char) size; + buf.data()[2] = (unsigned char) (size >> 8); + buf.data()[1] = (unsigned char) (size >> 16); + buf.data()[0] = (unsigned char) (size >> 24); + + outputBufferMutex.lock(); + outputBuffer.append(buf); + outputBufferMutex.unlock(); + emit outputBufferChanged(); } void IslInterface::sessionEvent_ServerCompleteList(const Event_ServerCompleteList &event) { - for (int i = 0; i < event.user_list_size(); ++i) { - ServerInfo_User temp(event.user_list(i)); - temp.set_server_id(serverId); - emit externalUserJoined(temp); - } - for (int i = 0; i < event.room_list_size(); ++i) { - const ServerInfo_Room &room = event.room_list(i); - for (int j = 0; j < room.user_list_size(); ++j) { - ServerInfo_User userInfo(room.user_list(j)); - userInfo.set_server_id(serverId); - emit externalRoomUserJoined(room.room_id(), userInfo); - } - for (int j = 0; j < room.game_list_size(); ++j) { - ServerInfo_Game gameInfo(room.game_list(j)); - gameInfo.set_server_id(serverId); - emit externalRoomGameListChanged(room.room_id(), gameInfo); - } - } + for (int i = 0; i < event.user_list_size(); ++i) { + ServerInfo_User temp(event.user_list(i)); + temp.set_server_id(serverId); + emit externalUserJoined(temp); + } + for (int i = 0; i < event.room_list_size(); ++i) { + const ServerInfo_Room &room = event.room_list(i); + for (int j = 0; j < room.user_list_size(); ++j) { + ServerInfo_User userInfo(room.user_list(j)); + userInfo.set_server_id(serverId); + emit externalRoomUserJoined(room.room_id(), userInfo); + } + for (int j = 0; j < room.game_list_size(); ++j) { + ServerInfo_Game gameInfo(room.game_list(j)); + gameInfo.set_server_id(serverId); + emit externalRoomGameListChanged(room.room_id(), gameInfo); + } + } } void IslInterface::sessionEvent_UserJoined(const Event_UserJoined &event) { - ServerInfo_User userInfo(event.user_info()); - userInfo.set_server_id(serverId); - emit externalUserJoined(userInfo); + ServerInfo_User userInfo(event.user_info()); + userInfo.set_server_id(serverId); + emit externalUserJoined(userInfo); } void IslInterface::sessionEvent_UserLeft(const Event_UserLeft &event) { - emit externalUserLeft(QString::fromStdString(event.name())); + emit externalUserLeft(QString::fromStdString(event.name())); } void IslInterface::roomEvent_UserJoined(int roomId, const Event_JoinRoom &event) { - ServerInfo_User userInfo(event.user_info()); - userInfo.set_server_id(serverId); - emit externalRoomUserJoined(roomId, userInfo); + ServerInfo_User userInfo(event.user_info()); + userInfo.set_server_id(serverId); + emit externalRoomUserJoined(roomId, userInfo); } void IslInterface::roomEvent_UserLeft(int roomId, const Event_LeaveRoom &event) { - emit externalRoomUserLeft(roomId, QString::fromStdString(event.name())); + emit externalRoomUserLeft(roomId, QString::fromStdString(event.name())); } void IslInterface::roomEvent_Say(int roomId, const Event_RoomSay &event) { - emit externalRoomSay(roomId, QString::fromStdString(event.name()), QString::fromStdString(event.message())); + emit externalRoomSay(roomId, QString::fromStdString(event.name()), QString::fromStdString(event.message())); } void IslInterface::roomEvent_ListGames(int roomId, const Event_ListGames &event) { - for (int i = 0; i < event.game_list_size(); ++i) { - ServerInfo_Game gameInfo(event.game_list(i)); - gameInfo.set_server_id(serverId); - emit externalRoomGameListChanged(roomId, gameInfo); - } -} - -void IslInterface::roomEvent_RemoveMessages(int roomId, const Event_RemoveMessages &event) -{ - emit externalRoomRemoveMessages(roomId, QString::fromStdString(event.name()), event.amount()); + for (int i = 0; i < event.game_list_size(); ++i) { + ServerInfo_Game gameInfo(event.game_list(i)); + gameInfo.set_server_id(serverId); + emit externalRoomGameListChanged(roomId, gameInfo); + } } void IslInterface::roomCommand_JoinGame(const Command_JoinGame &cmd, int cmdId, int roomId, qint64 sessionId) { - emit joinGameCommandReceived(cmd, cmdId, roomId, serverId, sessionId); + emit joinGameCommandReceived(cmd, cmdId, roomId, serverId, sessionId); } void IslInterface::processSessionEvent(const SessionEvent &event, qint64 sessionId) { - switch (getPbExtension(event)) { - case SessionEvent::SERVER_COMPLETE_LIST: - sessionEvent_ServerCompleteList(event.GetExtension(Event_ServerCompleteList::ext)); - break; - case SessionEvent::USER_JOINED: - sessionEvent_UserJoined(event.GetExtension(Event_UserJoined::ext)); - break; - case SessionEvent::USER_LEFT: - sessionEvent_UserLeft(event.GetExtension(Event_UserLeft::ext)); - break; - case SessionEvent::GAME_JOINED: { - QReadLocker clientsLocker(&server->clientsLock); - Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); - if (!client) { - qCDebug(IslInterfaceLog) << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; - break; - } - const Event_GameJoined &gameJoined = event.GetExtension(Event_GameJoined::ext); - client->playerAddedToGame(gameJoined.game_info().game_id(), gameJoined.game_info().room_id(), - gameJoined.player_id()); - client->sendProtocolItem(event); - break; - } - case SessionEvent::USER_MESSAGE: - case SessionEvent::REPLAY_ADDED: { - QReadLocker clientsLocker(&server->clientsLock); - Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); - if (!client) { - qCWarning(IslInterfaceLog) - << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; - break; - } - - client->sendProtocolItem(event); - break; - } - default:; - } + switch (getPbExtension(event)) { + case SessionEvent::SERVER_COMPLETE_LIST: sessionEvent_ServerCompleteList(event.GetExtension(Event_ServerCompleteList::ext)); break; + case SessionEvent::USER_JOINED: sessionEvent_UserJoined(event.GetExtension(Event_UserJoined::ext)); break; + case SessionEvent::USER_LEFT: sessionEvent_UserLeft(event.GetExtension(Event_UserLeft::ext)); break; + case SessionEvent::GAME_JOINED: { + QReadLocker clientsLocker(&server->clientsLock); + Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); + if (!client) { + qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; + break; + } + const Event_GameJoined &gameJoined = event.GetExtension(Event_GameJoined::ext); + client->playerAddedToGame(gameJoined.game_info().game_id(), gameJoined.game_info().room_id(), gameJoined.player_id()); + client->sendProtocolItem(event); + break; + } + case SessionEvent::USER_MESSAGE: + case SessionEvent::REPLAY_ADDED: { + QReadLocker clientsLocker(&server->clientsLock); + Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId); + if (!client) { + qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found"; + break; + } + + client->sendProtocolItem(event); + break; + } + default: ; + } } void IslInterface::processRoomEvent(const RoomEvent &event) { - switch (getPbExtension(event)) { - case RoomEvent::JOIN_ROOM: - roomEvent_UserJoined(event.room_id(), event.GetExtension(Event_JoinRoom::ext)); - break; - case RoomEvent::LEAVE_ROOM: - roomEvent_UserLeft(event.room_id(), event.GetExtension(Event_LeaveRoom::ext)); - break; - case RoomEvent::ROOM_SAY: - roomEvent_Say(event.room_id(), event.GetExtension(Event_RoomSay::ext)); - break; - case RoomEvent::LIST_GAMES: - roomEvent_ListGames(event.room_id(), event.GetExtension(Event_ListGames::ext)); - break; - case RoomEvent::REMOVE_MESSAGES: - roomEvent_RemoveMessages(event.room_id(), event.GetExtension(Event_RemoveMessages::ext)); - break; - default:; - } + switch (getPbExtension(event)) { + case RoomEvent::JOIN_ROOM: roomEvent_UserJoined(event.room_id(), event.GetExtension(Event_JoinRoom::ext)); break; + case RoomEvent::LEAVE_ROOM: roomEvent_UserLeft(event.room_id(), event.GetExtension(Event_LeaveRoom::ext)); break; + case RoomEvent::ROOM_SAY: roomEvent_Say(event.room_id(), event.GetExtension(Event_RoomSay::ext)); break; + case RoomEvent::LIST_GAMES: roomEvent_ListGames(event.room_id(), event.GetExtension(Event_ListGames::ext)); break; + default: ; + } } void IslInterface::processRoomCommand(const CommandContainer &cont, qint64 sessionId) { - for (int i = 0; i < cont.room_command_size(); ++i) { - const RoomCommand &roomCommand = cont.room_command(i); - switch (static_cast(getPbExtension(roomCommand))) { - case RoomCommand::JOIN_GAME: - roomCommand_JoinGame(roomCommand.GetExtension(Command_JoinGame::ext), cont.cmd_id(), cont.room_id(), - sessionId); - default:; - } - } + for (int i = 0; i < cont.room_command_size(); ++i) { + const RoomCommand &roomCommand = cont.room_command(i); + switch (static_cast(getPbExtension(roomCommand))) { + case RoomCommand::JOIN_GAME: roomCommand_JoinGame(roomCommand.GetExtension(Command_JoinGame::ext), cont.cmd_id(), cont.room_id(), sessionId); + default: ; + } + } } void IslInterface::processMessage(const IslMessage &item) { - qCDebug(IslInterfaceLog) << getSafeDebugString(item); - - switch (item.message_type()) { - case IslMessage::ROOM_COMMAND_CONTAINER: { - processRoomCommand(item.room_command(), item.session_id()); - break; - } - case IslMessage::GAME_COMMAND_CONTAINER: { - emit gameCommandContainerReceived(item.game_command(), item.player_id(), serverId, item.session_id()); - break; - } - case IslMessage::SESSION_EVENT: { - processSessionEvent(item.session_event(), item.session_id()); - break; - } - case IslMessage::RESPONSE: { - emit responseReceived(item.response(), item.session_id()); - break; - } - case IslMessage::GAME_EVENT_CONTAINER: { - emit gameEventContainerReceived(item.game_event_container(), item.session_id()); - break; - } - case IslMessage::ROOM_EVENT: { - processRoomEvent(item.room_event()); - break; - } - default:; - } + qDebug() << QString::fromStdString(item.DebugString()); + + switch (item.message_type()) { + case IslMessage::ROOM_COMMAND_CONTAINER: { + processRoomCommand(item.room_command(), item.session_id()); + break; + } + case IslMessage::GAME_COMMAND_CONTAINER: { + emit gameCommandContainerReceived(item.game_command(), item.player_id(), serverId, item.session_id()); + break; + } + case IslMessage::SESSION_EVENT: { + processSessionEvent(item.session_event(), item.session_id()); + break; + } + case IslMessage::RESPONSE: { + emit responseReceived(item.response(), item.session_id()); + break; + } + case IslMessage::GAME_EVENT_CONTAINER: { + emit gameEventContainerReceived(item.game_event_container(), item.session_id()); + break; + } + case IslMessage::ROOM_EVENT: { + processRoomEvent(item.room_event()); break; + break; + } + default: ; + } } diff --git a/servatrice/src/isl_interface.h b/servatrice/src/isl_interface.h index 27e5b8293..cd1787ff1 100644 --- a/servatrice/src/isl_interface.h +++ b/servatrice/src/isl_interface.h @@ -2,12 +2,11 @@ #define ISL_INTERFACE_H #include "servatrice.h" - #include #include -#include -#include -#include +#include "pb/serverinfo_user.pb.h" +#include "pb/serverinfo_room.pb.h" +#include "pb/serverinfo_game.pb.h" class Servatrice; class QSslSocket; @@ -22,81 +21,68 @@ class Event_JoinRoom; class Event_LeaveRoom; class Event_RoomSay; class Event_ListGames; -class Event_RemoveMessages; class Command_JoinGame; -class IslInterface : public QObject -{ - Q_OBJECT +class IslInterface : public QObject { + Q_OBJECT private slots: - void readClient(); - void catchSocketError(QAbstractSocket::SocketError socketError); - void flushOutputBuffer(); + void readClient(); + void catchSocketError(QAbstractSocket::SocketError socketError); + void flushOutputBuffer(); signals: - void outputBufferChanged(); - - void externalUserJoined(ServerInfo_User userInfo); - void externalUserLeft(QString userName); - void externalRoomUserJoined(int roomId, ServerInfo_User userInfo); - void externalRoomUserLeft(int roomId, QString userName); - void externalRoomSay(int roomId, QString userName, QString message); - void externalRoomGameListChanged(int roomId, ServerInfo_Game gameInfo); - void externalRoomRemoveMessages(int roomId, QString userName, int amount); - void joinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId); - void gameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId); - void responseReceived(const Response &resp, qint64 sessionId); - void gameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); - + void outputBufferChanged(); + + void externalUserJoined(ServerInfo_User userInfo); + void externalUserLeft(QString userName); + void externalRoomUserJoined(int roomId, ServerInfo_User userInfo); + void externalRoomUserLeft(int roomId, QString userName); + void externalRoomSay(int roomId, QString userName, QString message); + void externalRoomGameListChanged(int roomId, ServerInfo_Game gameInfo); + void joinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId); + void gameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId); + void responseReceived(const Response &resp, qint64 sessionId); + void gameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId); private: - int serverId; - int socketDescriptor; - QString peerHostName, peerAddress; - int peerPort; - QSslCertificate peerCert; - - QMutex outputBufferMutex; - Servatrice *server; - QSslSocket *socket; - - QByteArray inputBuffer, outputBuffer; - bool messageInProgress; - int messageLength; - - void sessionEvent_ServerCompleteList(const Event_ServerCompleteList &event); - void sessionEvent_UserJoined(const Event_UserJoined &event); - void sessionEvent_UserLeft(const Event_UserLeft &event); - - void roomEvent_UserJoined(int roomId, const Event_JoinRoom &event); - void roomEvent_UserLeft(int roomId, const Event_LeaveRoom &event); - void roomEvent_Say(int roomId, const Event_RoomSay &event); - void roomEvent_ListGames(int roomId, const Event_ListGames &event); - void roomEvent_RemoveMessages(int roomId, const Event_RemoveMessages &event); - - void roomCommand_JoinGame(const Command_JoinGame &cmd, int cmdId, int roomId, qint64 sessionId); - - void processSessionEvent(const SessionEvent &event, qint64 sessionId); - void processRoomEvent(const RoomEvent &event); - void processRoomCommand(const CommandContainer &cont, qint64 sessionId); - - void processMessage(const IslMessage &item); - void sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey); + int serverId; + int socketDescriptor; + QString peerHostName, peerAddress; + int peerPort; + QSslCertificate peerCert; + + QMutex outputBufferMutex; + Servatrice *server; + QSslSocket *socket; + + QByteArray inputBuffer, outputBuffer; + bool messageInProgress; + int messageLength; + + void sessionEvent_ServerCompleteList(const Event_ServerCompleteList &event); + void sessionEvent_UserJoined(const Event_UserJoined &event); + void sessionEvent_UserLeft(const Event_UserLeft &event); + + void roomEvent_UserJoined(int roomId, const Event_JoinRoom &event); + void roomEvent_UserLeft(int roomId, const Event_LeaveRoom &event); + void roomEvent_Say(int roomId, const Event_RoomSay &event); + void roomEvent_ListGames(int roomId, const Event_ListGames &event); + + void roomCommand_JoinGame(const Command_JoinGame &cmd, int cmdId, int roomId, qint64 sessionId); + + void processSessionEvent(const SessionEvent &event, qint64 sessionId); + void processRoomEvent(const RoomEvent &event); + void processRoomCommand(const CommandContainer &cont, qint64 sessionId); + + void processMessage(const IslMessage &item); + void sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey); public slots: - void initServer(); - void initClient(); - + void initServer(); + void initClient(); public: - IslInterface(int socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); - IslInterface(int _serverId, - const QString &peerHostName, - const QString &peerAddress, - int peerPort, - const QSslCertificate &peerCert, - const QSslCertificate &cert, - const QSslKey &privateKey, - Servatrice *_server); - ~IslInterface(); - - void transmitMessage(const IslMessage &item); + IslInterface(int socketDescriptor, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); + IslInterface(int _serverId, const QString &peerHostName, const QString &peerAddress, int peerPort, const QSslCertificate &peerCert, const QSslCertificate &cert, const QSslKey &privateKey, Servatrice *_server); + ~IslInterface(); + + void transmitMessage(const IslMessage &item); }; #endif diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index b1294a04c..d19ebb66f 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -18,22 +18,21 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "passwordhasher.h" #include "servatrice.h" #include "server_logger.h" #include "settingscache.h" #include "signalhandler.h" #include "smtpclient.h" +#include "rng_sfmt.h" #include "version_string.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include RNG_Abstract *rng; ServerLogger *logger; @@ -46,168 +45,177 @@ SmtpClient *smtpClient; void testRNG(); void testHash(); +#if QT_VERSION < 0x050000 +void myMessageOutput(QtMsgType type, const char *msg); +void myMessageOutput2(QtMsgType type, const char *msg); +#else void myMessageOutput(QtMsgType type, const QMessageLogContext &, const QString &msg); void myMessageOutput2(QtMsgType type, const QMessageLogContext &, const QString &msg); +#endif /* Implementations */ void testRNG() { - const int n = 500000; - std::cerr << "Testing random number generator (n = " << n << " * bins)..." << std::endl; - - const int min = 1; - const int minMax = 2; - const int maxMax = 10; - - QVector> numbers(maxMax - minMax + 1); - QVector chisq(maxMax - minMax + 1); - for (int max = minMax; max <= maxMax; ++max) { - numbers[max - minMax] = rng->makeNumbersVector(n * (max - min + 1), min, max); - chisq[max - minMax] = rng->testRandom(numbers[max - minMax]); - } - for (int i = 0; i <= maxMax - min; ++i) { - std::cerr << (min + i); - for (auto &number : numbers) { - if (i < number.size()) - std::cerr << "\t" << number[i]; - else - std::cerr << "\t"; - } - std::cerr << std::endl; - } - std::cerr << std::endl << "Chi^2 ="; - for (double j : chisq) - std::cerr << "\t" << QString::number(j, 'f', 3).toStdString(); - std::cerr << std::endl << "k ="; - for (int j = 0; j < chisq.size(); ++j) - std::cerr << "\t" << (j - min + minMax); - std::cerr << std::endl << std::endl; + const int n = 500000; + std::cerr << "Testing random number generator (n = " << n << " * bins)..." << std::endl; + + const int min = 1; + const int minMax = 2; + const int maxMax = 10; + + QVector > numbers(maxMax - minMax + 1); + QVector chisq(maxMax - minMax + 1); + for (int max = minMax; max <= maxMax; ++max) { + numbers[max - minMax] = rng->makeNumbersVector(n * (max - min + 1), min, max); + chisq[max - minMax] = rng->testRandom(numbers[max - minMax]); + } + for (int i = 0; i <= maxMax - min; ++i) { + std::cerr << (min + i); + for (int j = 0; j < numbers.size(); ++j) { + if (i < numbers[j].size()) + std::cerr << "\t" << numbers[j][i]; + else + std::cerr << "\t"; + } + std::cerr << std::endl; + } + std::cerr << std::endl << "Chi^2 ="; + for (int j = 0; j < chisq.size(); ++j) + std::cerr << "\t" << QString::number(chisq[j], 'f', 3).toStdString(); + std::cerr << std::endl << "k ="; + for (int j = 0; j < chisq.size(); ++j) + std::cerr << "\t" << (j - min + minMax); + std::cerr << std::endl << std::endl; } void testHash() { - const int n = 5000; - std::cerr << "Benchmarking password hash function (n =" << n << ")..." << std::endl; - QDateTime startTime = QDateTime::currentDateTime(); - for (int i = 0; i < n; ++i) - PasswordHasher::computeHash("aaaaaa", "aaaaaaaaaaaaaaaa"); - QDateTime endTime = QDateTime::currentDateTime(); - std::cerr << startTime.secsTo(endTime) << "secs" << std::endl; + const int n = 5000; + std::cerr << "Benchmarking password hash function (n =" << n << ")..." << std::endl; + QDateTime startTime = QDateTime::currentDateTime(); + for (int i = 0; i < n; ++i) + PasswordHasher::computeHash("aaaaaa", "aaaaaaaaaaaaaaaa"); + QDateTime endTime = QDateTime::currentDateTime(); + std::cerr << startTime.secsTo(endTime) << "secs" << std::endl; } +#if QT_VERSION < 0x050000 +void myMessageOutput(QtMsgType /*type*/, const char *msg) +{ + logger->logMessage(msg); +} + +void myMessageOutput2(QtMsgType /*type*/, const char *msg) +{ + logger->logMessage(msg); + std::cerr << msg << std::endl; +} +#else void myMessageOutput(QtMsgType /*type*/, const QMessageLogContext &, const QString &msg) { - logger->logMessage(msg); + logger->logMessage(msg); } void myMessageOutput2(QtMsgType /*type*/, const QMessageLogContext &, const QString &msg) { - logger->logMessage(msg); - std::cerr << msg.toStdString() << std::endl; + logger->logMessage(msg); + std::cerr << msg.toStdString() << std::endl; } +#endif int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); - QCoreApplication::setOrganizationName("Cockatrice"); - QCoreApplication::setApplicationName("Servatrice"); - QCoreApplication::setApplicationVersion(VERSION_STRING); + QCoreApplication app(argc, argv); + app.setOrganizationName("Cockatrice"); + app.setApplicationName("Servatrice"); + + QStringList args = app.arguments(); + bool testRandom = args.contains("--test-random"); + bool testHashFunction = args.contains("--test-hash"); + bool logToConsole = args.contains("--log-to-console"); + QString configPath; + int hasConfigPath=args.indexOf("--config"); + if(hasConfigPath > -1 && args.count() > hasConfigPath + 1) + configPath = args.at(hasConfigPath + 1); + + qRegisterMetaType >("QList"); - QCommandLineParser parser; - parser.addHelpOption(); - parser.addVersionOption(); +#if QT_VERSION < 0x050000 + // gone in Qt5, all source files _MUST_ be utf8-encoded + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); +#endif - QCommandLineOption testRandomOpt("test-random", "Test PRNG (chi^2)"); - parser.addOption(testRandomOpt); + configPath = SettingsCache::guessConfigurationPath(configPath); + qWarning() << "Using configuration file: " << configPath; + settingsCache = new SettingsCache(configPath); + + loggerThread = new QThread; + loggerThread->setObjectName("logger"); + logger = new ServerLogger(logToConsole); + logger->moveToThread(loggerThread); + + loggerThread->start(); + QMetaObject::invokeMethod(logger, "startLog", Qt::BlockingQueuedConnection, Q_ARG(QString, settingsCache->value("server/logfile", QString("server.log")).toString())); - QCommandLineOption testHashFunctionOpt("test-hash", "Test password hash function"); - parser.addOption(testHashFunctionOpt); +#if QT_VERSION < 0x050000 + if (logToConsole) + qInstallMsgHandler(myMessageOutput); + else + qInstallMsgHandler(myMessageOutput2); +#else + if (logToConsole) + qInstallMessageHandler(myMessageOutput); + else + qInstallMessageHandler(myMessageOutput2); +#endif - QCommandLineOption logToConsoleOpt("log-to-console", "Write server logs to console"); - parser.addOption(logToConsoleOpt); + signalhandler = new SignalHandler(); - QCommandLineOption configPathOpt("config", "Read server configuration from ", "file", ""); - parser.addOption(configPathOpt); + rng = new RNG_SFMT; + + std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl; + std::cerr << "-------------------------" << std::endl; + + PasswordHasher::initialize(); + + if (testRandom) + testRNG(); + if (testHashFunction) + testHash(); - parser.process(app); + smtpClient = new SmtpClient(); + + Servatrice *server = new Servatrice(); + QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection); + int retval = 0; + if (server->initServer()) { + std::cerr << "-------------------------" << std::endl; + std::cerr << "Server initialized." << std::endl; - bool testRandom = parser.isSet(testRandomOpt); - bool testHashFunction = parser.isSet(testHashFunctionOpt); - bool logToConsole = parser.isSet(logToConsoleOpt); - QString configPath = parser.value(configPathOpt); +#if QT_VERSION < 0x050000 + qInstallMsgHandler(myMessageOutput); +#else + qInstallMessageHandler(myMessageOutput); +#endif - qRegisterMetaType>("QList"); + retval = app.exec(); + + std::cerr << "Server quit." << std::endl; + std::cerr << "-------------------------" << std::endl; + } + + delete smtpClient; + delete rng; + delete signalhandler; + delete settingsCache; + + logger->deleteLater(); + loggerThread->wait(); + delete loggerThread; - if (configPath.isEmpty()) { - configPath = SettingsCache::guessConfigurationPath(); - } else if (!QFile::exists(configPath)) { - qCritical() << "Could not find configuration file at" << configPath; - return 1; - } - qWarning() << "Using configuration file: " << configPath; - settingsCache = new SettingsCache(configPath); + // Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); - loggerThread = new QThread; - loggerThread->setObjectName("logger"); - logger = new ServerLogger(logToConsole); - logger->moveToThread(loggerThread); - - loggerThread->start(); - QMetaObject::invokeMethod(logger, "startLog", Qt::BlockingQueuedConnection, - Q_ARG(QString, settingsCache->value("server/logfile", QString("server.log")).toString())); - - if (logToConsole) - qInstallMessageHandler(myMessageOutput); - else - qInstallMessageHandler(myMessageOutput2); - - signalhandler = new SignalHandler(); - - rng = new RNG_SFMT; - - std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl; - std::cerr << "-------------------------" << std::endl; - - if (testRandom) { - testRNG(); - } - if (testHashFunction) { - testHash(); - } - if (testRandom || testHashFunction) { - return 0; - } - - smtpClient = new SmtpClient(); - - auto *server = new Servatrice(); - QObject::connect(server, SIGNAL(destroyed()), &app, SLOT(quit()), Qt::QueuedConnection); - int retval = 0; - if (server->initServer()) { - std::cerr << "-------------------------" << std::endl; - std::cerr << "Server initialized." << std::endl; - - qInstallMessageHandler(myMessageOutput); - - retval = QCoreApplication::exec(); - - std::cerr << "Server quit." << std::endl; - std::cerr << "-------------------------" << std::endl; - } - - delete smtpClient; - delete rng; - delete signalhandler; - delete settingsCache; - - logger->deleteLater(); - loggerThread->wait(); - delete loggerThread; - - // Delete all global objects allocated by libprotobuf. - google::protobuf::ShutdownProtobufLibrary(); - - QCoreApplication::quit(); - return retval; + return retval; } diff --git a/servatrice/src/passwordhasher.cpp b/servatrice/src/passwordhasher.cpp new file mode 100644 index 000000000..41e73b9e8 --- /dev/null +++ b/servatrice/src/passwordhasher.cpp @@ -0,0 +1,76 @@ +#include "passwordhasher.h" + +#if QT_VERSION < 0x050000 + #include + #include + #include +#endif + +#include +#include "rng_sfmt.h" + +void PasswordHasher::initialize() +{ +#if QT_VERSION < 0x050000 + // These calls are required by libgcrypt before we use any of its functions. + gcry_check_version(0); + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +#endif +} + +#if QT_VERSION < 0x050000 +QString PasswordHasher::computeHash(const QString &password, const QString &salt) +{ + const int algo = GCRY_MD_SHA512; + const int rounds = 1000; + + QByteArray passwordBuffer = (salt + password).toUtf8(); + int hashLen = gcry_md_get_algo_dlen(algo); + char *hash = new char[hashLen], *tmp = new char[hashLen]; + gcry_md_hash_buffer(algo, hash, passwordBuffer.data(), passwordBuffer.size()); + for (int i = 1; i < rounds; ++i) { + memcpy(tmp, hash, hashLen); + gcry_md_hash_buffer(algo, hash, tmp, hashLen); + } + QString hashedPass = salt + QString(QByteArray(hash, hashLen).toBase64()); + delete[] tmp; + delete[] hash; + return hashedPass; +} +#else +QString PasswordHasher::computeHash(const QString &password, const QString &salt) +{ + QCryptographicHash::Algorithm algo = QCryptographicHash::Sha512; + const int rounds = 1000; + + QByteArray hash = (salt + password).toUtf8(); + for (int i = 0; i < rounds; ++i) { + hash = QCryptographicHash::hash(hash, algo); + } + QString hashedPass = salt + QString(hash.toBase64()); + return hashedPass; +} +#endif + +QString PasswordHasher::generateRandomSalt(const int len) +{ + static const char alphanum[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; + + QString ret; + int size = sizeof(alphanum) - 1; + + for (int i = 0; i < len; ++i) { + ret.append(alphanum[rng->rand(0, size)]); + } + + return ret; +} + +QString PasswordHasher::generateActivationToken() +{ + return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16); +} \ No newline at end of file diff --git a/servatrice/src/passwordhasher.h b/servatrice/src/passwordhasher.h new file mode 100644 index 000000000..4160cb0f0 --- /dev/null +++ b/servatrice/src/passwordhasher.h @@ -0,0 +1,14 @@ +#ifndef PASSWORDHASHER_H +#define PASSWORDHASHER_H + +#include + +class PasswordHasher { +public: + static void initialize(); + static QString computeHash(const QString &password, const QString &salt); + static QString generateRandomSalt(const int len = 16); + static QString generateActivationToken(); +}; + +#endif diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 410bf4ed9..95e01ef66 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -17,54 +17,55 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "servatrice.h" - -#include "email_parser.h" -#include "isl_interface.h" -#include "main.h" -#include "servatrice_connection_pool.h" -#include "servatrice_database_interface.h" -#include "server_logger.h" -#include "serversocketinterface.h" -#include "settingscache.h" -#include "smtpclient.h" - +#include +#include +#include #include #include -#include -#include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include "servatrice.h" +#include "servatrice_database_interface.h" +#include "servatrice_connection_pool.h" +#include "server_room.h" +#include "settingscache.h" +#include "serversocketinterface.h" +#include "isl_interface.h" +#include "server_logger.h" +#include "main.h" +#include "decklist.h" +#include "smtpclient.h" +#include "pb/event_server_message.pb.h" +#include "pb/event_server_shutdown.pb.h" +#include "pb/event_connection_closed.pb.h" +#include "featureset.h" -Servatrice_GameServer::Servatrice_GameServer(Servatrice *_server, - int _numberPools, - const QSqlDatabase &_sqlDatabase, - QObject *parent) - : QTcpServer(parent), server(_server) +Servatrice_GameServer::Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent) + : QTcpServer(parent), + server(_server) { - for (int i = 0; i < _numberPools; ++i) { - auto newDatabaseInterface = new Servatrice_DatabaseInterface(i, server); - auto newPool = new Servatrice_ConnectionPool(newDatabaseInterface); + if (_numberPools == 0) { + server->setThreaded(false); + Servatrice_DatabaseInterface *newDatabaseInterface = new Servatrice_DatabaseInterface(0, server); + Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface); - auto newThread = new QThread; + server->addDatabaseInterface(thread(), newDatabaseInterface); + newDatabaseInterface->initDatabase(_sqlDatabase); + + connectionPools.append(newPool); + } else + for (int i = 0; i < _numberPools; ++i) { + Servatrice_DatabaseInterface *newDatabaseInterface = new Servatrice_DatabaseInterface(i, server); + Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface); + + QThread *newThread = new QThread; newThread->setObjectName("pool_" + QString::number(i)); newPool->moveToThread(newThread); newDatabaseInterface->moveToThread(newThread); server->addDatabaseInterface(newThread, newDatabaseInterface); newThread->start(); - QMetaObject::invokeMethod(newDatabaseInterface, "initDatabase", Qt::BlockingQueuedConnection, - Q_ARG(QSqlDatabase, _sqlDatabase)); + QMetaObject::invokeMethod(newDatabaseInterface, "initDatabase", Qt::BlockingQueuedConnection, Q_ARG(QSqlDatabase, _sqlDatabase)); connectionPools.append(newPool); } @@ -77,16 +78,31 @@ Servatrice_GameServer::~Servatrice_GameServer() QThread *poolThread = connectionPools[i]->thread(); connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() poolThread->wait(); - poolThread->deleteLater(); } } +#if QT_VERSION < 0x050000 +void Servatrice_GameServer::incomingConnection(int socketDescriptor) +#else void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor) +#endif { - Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); + // Determine connection pool with smallest client count + int minClientCount = -1; + int poolIndex = -1; + QStringList debugStr; + for (int i = 0; i < connectionPools.size(); ++i) { + const int clientCount = connectionPools[i]->getClientCount(); + if ((poolIndex == -1) || (clientCount < minClientCount)) { + minClientCount = clientCount; + poolIndex = i; + } + debugStr.append(QString::number(clientCount)); + } + qDebug() << "Pool utilisation:" << debugStr; + Servatrice_ConnectionPool *pool = connectionPools[poolIndex]; - auto ssi = new TcpServerSocketInterface(server, pool->getDatabaseInterface()); - connect(ssi, SIGNAL(incTxBytes(qint64)), this, SLOT(incTxBytes(qint64))); + ServerSocketInterface *ssi = new ServerSocketInterface(server, pool->getDatabaseInterface()); ssi->moveToThread(pool->thread()); pool->addClient(); connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient())); @@ -94,103 +110,16 @@ void Servatrice_GameServer::incomingConnection(qintptr socketDescriptor) QMetaObject::invokeMethod(ssi, "initConnection", Qt::QueuedConnection, Q_ARG(int, socketDescriptor)); } -Servatrice_ConnectionPool *Servatrice_GameServer::findLeastUsedConnectionPool() -{ - int minClientCount = -1; - int poolIndex = -1; - QStringList debugStr; - for (int i = 0; i < connectionPools.size(); ++i) { - const int clientCount = connectionPools[i]->getClientCount(); - if ((poolIndex == -1) || (clientCount < minClientCount)) { - minClientCount = clientCount; - poolIndex = i; - } - debugStr.append(QString::number(clientCount)); - } - qDebug().noquote() << "Pool utilisation:" << debugStr.join(", "); - return connectionPools[poolIndex]; -} - -#define WEBSOCKET_POOL_NUMBER 999 - -Servatrice_WebsocketGameServer::Servatrice_WebsocketGameServer(Servatrice *_server, - int _numberPools, - const QSqlDatabase &_sqlDatabase, - QObject *parent) - : QWebSocketServer("Servatrice", QWebSocketServer::NonSecureMode, parent), server(_server) -{ - for (int i = 0; i < _numberPools; ++i) { - int poolNumber = WEBSOCKET_POOL_NUMBER + i; - auto newDatabaseInterface = new Servatrice_DatabaseInterface(poolNumber, server); - auto newPool = new Servatrice_ConnectionPool(newDatabaseInterface); - - auto newThread = new QThread; - newThread->setObjectName("pool_" + QString::number(poolNumber)); - newPool->moveToThread(newThread); - newDatabaseInterface->moveToThread(newThread); - server->addDatabaseInterface(newThread, newDatabaseInterface); - - newThread->start(); - QMetaObject::invokeMethod(newDatabaseInterface, "initDatabase", Qt::BlockingQueuedConnection, - Q_ARG(QSqlDatabase, _sqlDatabase)); - - connectionPools.append(newPool); - - connect(this, SIGNAL(newConnection()), this, SLOT(onNewConnection())); - } -} - -Servatrice_WebsocketGameServer::~Servatrice_WebsocketGameServer() -{ - for (int i = 0; i < connectionPools.size(); ++i) { - logger->logMessage(QString("Closing websocket pool %1...").arg(i)); - QThread *poolThread = connectionPools[i]->thread(); - connectionPools[i]->deleteLater(); // pool destructor calls thread()->quit() - poolThread->wait(); - poolThread->deleteLater(); - } -} - -void Servatrice_WebsocketGameServer::onNewConnection() -{ - Servatrice_ConnectionPool *pool = findLeastUsedConnectionPool(); - - auto ssi = new WebsocketServerSocketInterface(server, pool->getDatabaseInterface()); - connect(ssi, SIGNAL(incTxBytes(quint64)), this, SLOT(incTxBytes(quint64))); - /* - * Due to a Qt limitation, websockets can't be moved to another thread. - * This will hopefully change in Qt6 if QtWebSocket will be integrated in QtNetwork - */ - // ssi->moveToThread(pool->thread()); - pool->addClient(); - connect(ssi, SIGNAL(destroyed()), pool, SLOT(removeClient())); - - QMetaObject::invokeMethod(ssi, "initConnection", Qt::QueuedConnection, Q_ARG(void *, nextPendingConnection())); -} - -Servatrice_ConnectionPool *Servatrice_WebsocketGameServer::findLeastUsedConnectionPool() -{ - int minClientCount = -1; - int poolIndex = -1; - QStringList debugStr; - for (int i = 0; i < connectionPools.size(); ++i) { - const int clientCount = connectionPools[i]->getClientCount(); - if ((poolIndex == -1) || (clientCount < minClientCount)) { - minClientCount = clientCount; - poolIndex = i; - } - debugStr.append(QString::number(clientCount)); - } - qDebug().noquote() << "Pool utilisation:" << debugStr.join(", "); - return connectionPools[poolIndex]; -} - +#if QT_VERSION < 0x050000 +void Servatrice_IslServer::incomingConnection(int socketDescriptor) +#else void Servatrice_IslServer::incomingConnection(qintptr socketDescriptor) +#endif { - auto thread = new QThread; + QThread *thread = new QThread; connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - auto interface = new IslInterface(static_cast(socketDescriptor), cert, privateKey, server); + IslInterface *interface = new IslInterface(socketDescriptor, cert, privateKey, server); interface->moveToThread(thread); connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); @@ -199,8 +128,7 @@ void Servatrice_IslServer::incomingConnection(qintptr socketDescriptor) } Servatrice::Servatrice(QObject *parent) - : Server(parent), authenticationMethod(AuthenticationNone), uptime(0), txBytes(0), rxBytes(0), - shutdownTimer(nullptr) + : Server(true, parent), uptime(0), shutdownTimer(0), isFirstShutdownMessage(true) { qRegisterMetaType("QSqlDatabase"); } @@ -208,147 +136,155 @@ Servatrice::Servatrice(QObject *parent) Servatrice::~Servatrice() { gameServer->close(); - - // we are destroying the clients outside their thread! - for (auto *client : clients) { - client->prepareDestroy(); - } - - if (shutdownTimer) { - shutdownTimer->deleteLater(); - } - - servatriceDatabaseInterface->deleteLater(); prepareDestroy(); } bool Servatrice::initServer() { + serverName = settingsCache->value("server/name", "My Cockatrice server").toString(); + serverId = settingsCache->value("server/id", 0).toInt(); + clientIdRequired = settingsCache->value("server/requireclientid",0).toBool(); + regServerOnly = settingsCache->value("authentication/regonly", 0).toBool(); - serverId = getServerID(); - if (getAuthenticationMethodString() == "sql") { + const QString authenticationMethodStr = settingsCache->value("authentication/method").toString(); + if (authenticationMethodStr == "sql") { qDebug() << "Authenticating method: sql"; authenticationMethod = AuthenticationSql; - } else if (getAuthenticationMethodString() == "password") { + } else if(authenticationMethodStr == "password") { qDebug() << "Authenticating method: password"; authenticationMethod = AuthenticationPassword; } else { - if (getRegOnlyServerEnabled()) { + if (regServerOnly) { qDebug() << "Registration only server enabled but no authentication method defined: Error."; return false; } + qDebug() << "Authenticating method: none"; authenticationMethod = AuthenticationNone; } - qDebug() << "Store Replays:" << getStoreReplaysEnabled(); - qDebug() << "Client ID Required:" << getClientIDRequiredEnabled(); - qDebug() << "Maximum user limit enabled:" << getMaxUserLimitEnabled(); + qDebug() << "Store Replays: " << settingsCache->value("game/store_replays", true).toBool(); + qDebug() << "Client ID Required: " << clientIdRequired; + bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool(); + qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled; - if (getMaxUserLimitEnabled()) { - qDebug() << "Maximum total user limit:" << getMaxUserTotal(); - qDebug() << "Maximum tcp user limit:" << getMaxTcpUserLimit(); - qDebug() << "Maximum websocket user limit:" << getMaxWebSocketUserLimit(); + if (maxUserLimitEnabled){ + int maxUserLimit = settingsCache->value("security/max_users_total", 500).toInt(); + qDebug() << "Maximum user limit: " << maxUserLimit; } - qDebug() << "Accept registered users only:" << getRegOnlyServerEnabled(); - qDebug() << "Registration enabled:" << getRegistrationEnabled(); - if (getRegistrationEnabled()) { - QStringList emailBlackListFilters = getEmailBlackList().split(",", Qt::SkipEmptyParts); - QStringList emailWhiteListFilters = getEmailWhiteList().split(",", Qt::SkipEmptyParts); - qDebug() << "Email blacklist:" << emailBlackListFilters; - qDebug() << "Email whitelist:" << emailWhiteListFilters; - qDebug() << "Require email address to register:" << getRequireEmailForRegistrationEnabled(); - qDebug() << "Require email activation via token:" << getRequireEmailActivationEnabled(); - if (getMaxAccountsPerEmail()) { - qDebug() << "Maximum number of accounts per email:" << getMaxAccountsPerEmail(); - } else { - qDebug() << "Maximum number of accounts per email: unlimited"; - } - qDebug() << "Enable Internal SMTP Client:" << getEnableInternalSMTPClient(); - if (!getEnableInternalSMTPClient()) { - qDebug() << "WARNING: Registrations are enabled but internal SMTP client is disabled. Users activation " - "emails will not be automatically mailed to users!"; - } - } + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); + bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); - qDebug() << "Reset password enabled:" << getEnableForgotPassword(); - if (getEnableForgotPassword()) { - qDebug() << "Reset password token life (in minutes):" << getForgotPasswordTokenLife(); - qDebug() << "Reset password challenge on:" << getEnableForgotPasswordChallenge(); - } + qDebug() << "Accept registered users only: " << regServerOnly; + qDebug() << "Registration enabled: " << registrationEnabled; + if (registrationEnabled) + qDebug() << "Require email address to register: " << requireEmailForRegistration; - qDebug() << "Auditing enabled:" << getEnableAudit(); - if (getEnableAudit()) { - qDebug() << "Audit registration attempts enabled:" << getEnableRegistrationAudit(); - qDebug() << "Audit reset password attepts enabled:" << getEnableForgotPasswordAudit(); - } + FeatureSet features; + features.initalizeFeatureList(serverRequiredFeatureList); + requiredFeatures = settingsCache->value("server/requiredfeatures","").toString(); + QStringList listReqFeatures = requiredFeatures.split(",", QString::SkipEmptyParts); + if (!listReqFeatures.isEmpty()) + foreach(QString reqFeature, listReqFeatures) + features.enableRequiredFeature(serverRequiredFeatureList,reqFeature); - if (getDBTypeString() == "mysql") { + qDebug() << "Required client features: " << serverRequiredFeatureList; + + QString dbTypeStr = settingsCache->value("database/type").toString(); + if (dbTypeStr == "mysql") databaseType = DatabaseMySql; - } else { + else databaseType = DatabaseNone; - } + servatriceDatabaseInterface = new Servatrice_DatabaseInterface(-1, this); setDatabaseInterface(servatriceDatabaseInterface); if (databaseType != DatabaseNone) { - dbPrefix = getDBPrefixString(); - bool dbOpened = servatriceDatabaseInterface->initDatabase( - "QMYSQL", getDBHostNameString(), getDBDatabaseNameString(), getDBUserNameString(), getDBPasswordString()); + settingsCache->beginGroup("database"); + dbPrefix = settingsCache->value("prefix").toString(); + bool dbOpened = + servatriceDatabaseInterface->initDatabase("QMYSQL", + settingsCache->value("hostname").toString(), + settingsCache->value("database").toString(), + settingsCache->value("user").toString(), + settingsCache->value("password").toString()); + settingsCache->endGroup(); if (!dbOpened) { qDebug() << "Failed to open database"; return false; } + updateServerList(); + qDebug() << "Clearing previous sessions..."; servatriceDatabaseInterface->clearSessionTables(); } - if (getRoomsMethodString() == "sql") { - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery( - "select id, name, descr, permissionlevel, privlevel, auto_join, join_message, chat_history_size from " - "{prefix}_rooms where id_server = :id_server order by id asc"); - query->bindValue(":id_server", serverId); + const QString roomMethod = settingsCache->value("rooms/method").toString(); + if (roomMethod == "sql") { + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, name, descr, permissionlevel, auto_join, join_message, chat_history_size from {prefix}_rooms order by id asc"); servatriceDatabaseInterface->execSqlQuery(query); while (query->next()) { - QSqlQuery *query2 = servatriceDatabaseInterface->prepareQuery( - "select name from {prefix}_rooms_gametypes where id_room = :id_room AND id_server = :id_server"); - query2->bindValue(":id_server", serverId); + QSqlQuery *query2 = servatriceDatabaseInterface->prepareQuery("select name from {prefix}_rooms_gametypes where id_room = :id_room"); query2->bindValue(":id_room", query->value(0).toInt()); servatriceDatabaseInterface->execSqlQuery(query2); QStringList gameTypes; while (query2->next()) gameTypes.append(query2->value(0).toString()); - addRoom(new Server_Room(query->value(0).toInt(), query->value(7).toInt(), query->value(1).toString(), - query->value(2).toString(), query->value(3).toString().toLower(), - query->value(4).toString().toLower(), static_cast(query->value(5).toInt()), - query->value(6).toString(), gameTypes, this)); + + addRoom(new Server_Room(query->value(0).toInt(), + query->value(6).toInt(), + query->value(1).toString(), + query->value(2).toString(), + query->value(3).toString().toLower(), + query->value(4).toInt(), + query->value(5).toString(), + gameTypes, + this + )); } } else { int size = settingsCache->beginReadArray("rooms/roomlist"); for (int i = 0; i < size; ++i) { settingsCache->setArrayIndex(i); + QStringList gameTypes; int size2 = settingsCache->beginReadArray("game_types"); - for (int j = 0; j < size2; ++j) { + for (int j = 0; j < size2; ++j) { settingsCache->setArrayIndex(j); gameTypes.append(settingsCache->value("name").toString()); } settingsCache->endArray(); + Server_Room *newRoom = new Server_Room( - i, settingsCache->value("chathistorysize").toInt(), settingsCache->value("name").toString(), + i, + settingsCache->value("chathistorysize").toInt(), + settingsCache->value("name").toString(), settingsCache->value("description").toString(), settingsCache->value("permissionlevel").toString().toLower(), - settingsCache->value("privilegelevel").toString().toLower(), settingsCache->value("autojoin").toBool(), - settingsCache->value("joinmessage").toString(), gameTypes, this); + settingsCache->value("autojoin").toBool(), + settingsCache->value("joinmessage").toString(), + gameTypes, + this + ); addRoom(newRoom); } - if (size == 0) { + if(size==0) + { // no room defined in config, add a dummy one - Server_Room *newRoom = new Server_Room(0, 100, "General room", "Play anything here.", "none", "none", true, - "", QStringList("Standard"), this); + Server_Room *newRoom = new Server_Room( + 0, + 100, + "General room", + "Play anything here.", + "none", + true, + "", + QStringList("Standard"), + this + ); addRoom(newRoom); } @@ -357,112 +293,100 @@ bool Servatrice::initServer() updateLoginMessage(); - try { - if (getISLNetworkEnabled()) { - qDebug() << "Connecting to ISL network."; - qDebug() << "Loading certificate..."; - QFile certFile(getISLNetworkSSLCertFile()); - if (!certFile.open(QIODevice::ReadOnly)) - throw QString("Error opening certificate file: %1").arg(getISLNetworkSSLCertFile()); - QSslCertificate cert(&certFile); + maxGameInactivityTime = settingsCache->value("game/max_game_inactivity_time", 120).toInt(); + maxPlayerInactivityTime = settingsCache->value("server/max_player_inactivity_time", 15).toInt(); + pingClockInterval = settingsCache->value("server/clientkeepalive", 1).toInt(); + maxUsersPerAddress = settingsCache->value("security/max_users_per_address", 4).toInt(); + messageCountingInterval = settingsCache->value("security/message_counting_interval", 10).toInt(); + maxMessageCountPerInterval = settingsCache->value("security/max_message_count_per_interval", 15).toInt(); + maxMessageSizePerInterval = settingsCache->value("security/max_message_size_per_interval", 1000).toInt(); + maxGamesPerUser = settingsCache->value("security/max_games_per_user", 5).toInt(); + commandCountingInterval = settingsCache->value("game/command_counting_interval", 10).toInt(); + maxCommandCountPerInterval = settingsCache->value("game/max_command_count_per_interval", 20).toInt(); - const QDateTime currentTime = QDateTime::currentDateTime(); - if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate() || cert.isBlacklisted()) - throw QString("Invalid certificate."); + try { if (settingsCache->value("servernetwork/active", 0).toInt()) { + qDebug() << "Connecting to ISL network."; + const QString certFileName = settingsCache->value("servernetwork/ssl_cert").toString(); + const QString keyFileName = settingsCache->value("servernetwork/ssl_key").toString(); + qDebug() << "Loading certificate..."; + QFile certFile(certFileName); + if (!certFile.open(QIODevice::ReadOnly)) + throw QString("Error opening certificate file: %1").arg(certFileName); + QSslCertificate cert(&certFile); +#if QT_VERSION < 0x050000 + if (!cert.isValid()) + throw(QString("Invalid certificate.")); +#else + const QDateTime currentTime = QDateTime::currentDateTime(); + if(currentTime < cert.effectiveDate() || + currentTime > cert.expiryDate() || + cert.isBlacklisted()) + throw(QString("Invalid certificate.")); +#endif + qDebug() << "Loading private key..."; + QFile keyFile(keyFileName); + if (!keyFile.open(QIODevice::ReadOnly)) + throw QString("Error opening private key file: %1").arg(keyFileName); + QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + if (key.isNull()) + throw QString("Invalid private key."); - qDebug() << "Loading private key..."; - QFile keyFile(getISLNetworkSSLKeyFile()); - if (!keyFile.open(QIODevice::ReadOnly)) - throw QString("Error opening private key file: %1").arg(getISLNetworkSSLKeyFile()); - QSslKey key(&keyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); - if (key.isNull()) - throw QString("Invalid private key."); - - QMutableListIterator serverIterator(serverList); - while (serverIterator.hasNext()) { - const ServerProperties &prop = serverIterator.next(); - if (prop.cert == cert) { - serverIterator.remove(); - continue; - } - - auto *thread = new QThread; - thread->setObjectName("isl_" + QString::number(prop.id)); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), - prop.controlPort, prop.cert, cert, key, this); - interface->moveToThread(thread); - connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); - - thread->start(); - QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); + QMutableListIterator serverIterator(serverList); + while (serverIterator.hasNext()) { + const ServerProperties &prop = serverIterator.next(); + if (prop.cert == cert) { + serverIterator.remove(); + continue; } - qDebug() << "Starting ISL server on port" << getISLNetworkPort(); - islServer = new Servatrice_IslServer(this, cert, key, this); - if (islServer->listen(QHostAddress::Any, static_cast(getISLNetworkPort()))) - qDebug() << "ISL server listening."; - else - throw QString("islServer->listen()"); + QThread *thread = new QThread; + thread->setObjectName("isl_" + QString::number(prop.id)); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this); + interface->moveToThread(thread); + connect(interface, SIGNAL(destroyed()), thread, SLOT(quit())); + + thread->start(); + QMetaObject::invokeMethod(interface, "initClient", Qt::BlockingQueuedConnection); } - } catch (QString &error) { + + const int networkPort = settingsCache->value("servernetwork/port", 14747).toInt(); + qDebug() << "Starting ISL server on port" << networkPort; + + islServer = new Servatrice_IslServer(this, cert, key, this); + if (islServer->listen(QHostAddress::Any, networkPort)) + qDebug() << "ISL server listening."; + else + throw QString("islServer->listen()"); + } } catch (QString error) { qDebug() << "ERROR --" << error; return false; } pingClock = new QTimer(this); connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); - pingClock->start(getClientKeepAlive() * 1000); + pingClock->start(pingClockInterval * 1000); + int statusUpdateTime = settingsCache->value("server/statusupdate", 15000).toInt(); statusUpdateClock = new QTimer(this); connect(statusUpdateClock, SIGNAL(timeout()), this, SLOT(statusUpdate())); - if (getServerStatusUpdateTime() != 0) { - qDebug() << "Starting status update clock, interval" << getServerStatusUpdateTime() << "ms"; - statusUpdateClock->start(getServerStatusUpdateTime()); + if (statusUpdateTime != 0) { + qDebug() << "Starting status update clock, interval " << statusUpdateTime << " ms"; + statusUpdateClock->start(statusUpdateTime); } - // SOCKET SERVER - if (getNumberOfTCPPools() > 0) { - gameServer = - new Servatrice_GameServer(this, getNumberOfTCPPools(), servatriceDatabaseInterface->getDatabase(), this); - gameServer->setMaxPendingConnections(1000); - QHostAddress tcpHost = getServerTCPHost(); - qDebug() << "Starting server on host" << tcpHost.toString() << "port" << getServerTCPPort(); - if (gameServer->listen(tcpHost, static_cast(getServerTCPPort()))) - qDebug() << "Server listening."; - else { - qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); - return false; - } + const int numberPools = settingsCache->value("server/number_pools", 1).toInt(); + gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this); + gameServer->setMaxPendingConnections(1000); + const int gamePort = settingsCache->value("server/port", 4747).toInt(); + qDebug() << "Starting server on port" << gamePort; + if (gameServer->listen(QHostAddress::Any, gamePort)) + qDebug() << "Server listening."; + else { + qDebug() << "gameServer->listen(): Error:" << gameServer->errorString(); + return false; } - - // WEBSOCKET SERVER - if (getNumberOfWebSocketPools() > 0) { - websocketGameServer = new Servatrice_WebsocketGameServer(this, getNumberOfWebSocketPools(), - servatriceDatabaseInterface->getDatabase(), this); - websocketGameServer->setMaxPendingConnections(1000); - QHostAddress webSocketHost = getServerWebSocketHost(); - qDebug() << "Starting websocket server on host" << webSocketHost.toString() << "port" - << getServerWebSocketPort(); - if (websocketGameServer->listen(webSocketHost, static_cast(getServerWebSocketPort()))) - qDebug() << "Websocket server listening."; - else { - qDebug() << "websocketGameServer->listen(): Error:" << websocketGameServer->errorString(); - return false; - } - } - - if (getIdleClientTimeout() > 0) { - qDebug() << "Idle client timeout value:" << getIdleClientTimeout(); - if (getIdleClientTimeout() < 300) - qDebug() << "WARNING: It is not recommended to set the IdleClientTimeout value very low. Doing so will " - "cause clients to very quickly be disconnected. Many players when connected may be searching " - "for card details outside the client in the middle of matches or possibly drafting outside the " - "client and short time out values will remove these players."; - } - - setRequiredFeatures(getRequiredFeatures()); return true; } @@ -478,21 +402,12 @@ void Servatrice::updateServerList() serverListMutex.lock(); serverList.clear(); - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery( - "select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select id, ssl_cert, hostname, address, game_port, control_port from {prefix}_servers order by id asc"); servatriceDatabaseInterface->execSqlQuery(query); while (query->next()) { - ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), - query->value(2).toString(), QHostAddress(query->value(3).toString()), - query->value(4).toInt(), query->value(5).toInt()); + ServerProperties prop(query->value(0).toInt(), QSslCertificate(query->value(1).toString().toUtf8()), query->value(2).toString(), QHostAddress(query->value(3).toString()), query->value(4).toInt(), query->value(5).toInt()); serverList.append(prop); - qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6") - .arg(prop.id) - .arg(QString(prop.cert.digest().toHex())) - .arg(prop.hostname) - .arg(prop.address.toString()) - .arg(prop.gamePort) - .arg(prop.controlPort); + qDebug() << QString("#%1 CERT=%2 NAME=%3 IP=%4:%5 CPORT=%6").arg(prop.id).arg(QString(prop.cert.digest().toHex())).arg(prop.hostname).arg(prop.address.toString()).arg(prop.gamePort).arg(prop.controlPort); } serverListMutex.unlock(); @@ -511,20 +426,20 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const { int result = 0; QReadLocker locker(&clientsLock); - for (auto client : clients) - if (static_cast(client)->getPeerAddress() == address) - ++result; + for (int i = 0; i < clients.size(); ++i) + if (static_cast(clients[i])->getPeerAddress() == address) + ++result; return result; } -QList Servatrice::getUsersWithAddressAsList(const QHostAddress &address) const +QList Servatrice::getUsersWithAddressAsList(const QHostAddress &address) const { - QList result; + QList result; QReadLocker locker(&clientsLock); - for (auto client : clients) - if (static_cast(client)->getPeerAddress() == address) - result.append(static_cast(client)); + for (int i = 0; i < clients.size(); ++i) + if (static_cast(clients[i])->getPeerAddress() == address) + result.append(static_cast(clients[i])); return result; } @@ -533,8 +448,7 @@ void Servatrice::updateLoginMessage() if (!servatriceDatabaseInterface->checkSql()) return; - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery( - "select message from {prefix}_servermessages where id_server = :id_server order by timest desc limit 1"); + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select message from {prefix}_servermessages where id_server = :id_server order by timest desc limit 1"); query->bindValue(":id_server", serverId); if (servatriceDatabaseInterface->execSqlQuery(query)) if (query->next()) { @@ -554,31 +468,12 @@ void Servatrice::updateLoginMessage() } } -void Servatrice::setRequiredFeatures(const QString &featureList) -{ - FeatureSet features; - serverRequiredFeatureList.clear(); - features.initalizeFeatureList(serverRequiredFeatureList); - QStringList listReqFeatures = featureList.split(",", Qt::SkipEmptyParts); - if (!listReqFeatures.isEmpty()) - for (const QString &reqFeature : listReqFeatures) { - features.enableRequiredFeature(serverRequiredFeatureList, reqFeature); - } - - qDebug() << "Set required client features to:" << serverRequiredFeatureList; -} - void Servatrice::statusUpdate() { if (!servatriceDatabaseInterface->checkSql()) return; const int uc = getUsersCount(); // for correct mutex locking order - - const QStringList mods_info = getOnlineModeratorList(); - const int mc = mods_info.size(); - const QString ml = mods_info.join(", "); - const int gc = getGamesCount(); uptime += statusUpdateClock->interval() / 1000; @@ -592,62 +487,35 @@ void Servatrice::statusUpdate() rxBytes = 0; rxBytesMutex.unlock(); - QSqlQuery *query = servatriceDatabaseInterface->prepareQuery( - "insert into {prefix}_uptime (id_server, timest, uptime, users_count, mods_count, mods_list, games_count, " - "tx_bytes, rx_bytes) values(:id, NOW(), :uptime, :users_count, :mods_count, :mods_list, :games_count, :tx, " - ":rx)"); + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("insert into {prefix}_uptime (id_server, timest, uptime, users_count, games_count, tx_bytes, rx_bytes) values(:id, NOW(), :uptime, :users_count, :games_count, :tx, :rx)"); query->bindValue(":id", serverId); query->bindValue(":uptime", uptime); query->bindValue(":users_count", uc); - query->bindValue(":mods_count", mc); - query->bindValue(":mods_list", ml); query->bindValue(":games_count", gc); query->bindValue(":tx", tx); query->bindValue(":rx", rx); servatriceDatabaseInterface->execSqlQuery(query); - if (getRegistrationEnabled() && getEnableInternalSMTPClient()) { - if (getRequireEmailActivationEnabled()) { - auto servDbSelQuery = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from " - "{prefix}_activation_emails a left join " - "{prefix}_users b on a.name = b.name"); - if (!servatriceDatabaseInterface->execSqlQuery(servDbSelQuery)) - return; + // send activation emails + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); + bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); + if (registrationEnabled && requireEmailForRegistration) + { + QSqlQuery *query = servatriceDatabaseInterface->prepareQuery("select a.name, b.email, b.token from {prefix}_activation_emails a left join {prefix}_users b on a.name = b.name"); + if (!servatriceDatabaseInterface->execSqlQuery(query)) + return; - auto *queryDelete = - servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name"); + QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery("delete from {prefix}_activation_emails where name = :name"); - while (servDbSelQuery->next()) { - const QString userName = servDbSelQuery->value(0).toString(); - const auto emailAddress = EmailParser::getParsedEmailAddress(servDbSelQuery->value(1).toString()); - const QString token = servDbSelQuery->value(2).toString(); + while (query->next()) { + const QString userName = query->value(0).toString(); + const QString emailAddress = query->value(1).toString(); + const QString token = query->value(2).toString(); - if (smtpClient->enqueueActivationTokenMail(userName, emailAddress, token)) { - queryDelete->bindValue(":name", userName); - servatriceDatabaseInterface->execSqlQuery(queryDelete); - } - } - } - - if (getEnableForgotPassword()) { - auto *forgotPwQuery = servatriceDatabaseInterface->prepareQuery( - "select a.name, b.email, b.token from {prefix}_forgot_password a left join {prefix}_users b on a.name " - "= b.name where a.emailed = 0"); - if (!servatriceDatabaseInterface->execSqlQuery(forgotPwQuery)) - return; - - QSqlQuery *queryDelete = servatriceDatabaseInterface->prepareQuery( - "update {prefix}_forgot_password set emailed = 1 where name = :name"); - - while (forgotPwQuery->next()) { - const QString userName = forgotPwQuery->value(0).toString(); - const auto emailAddress = EmailParser::getParsedEmailAddress(forgotPwQuery->value(1).toString()); - const QString token = forgotPwQuery->value(2).toString(); - - if (smtpClient->enqueueForgotPasswordTokenMail(userName, emailAddress, token)) { - queryDelete->bindValue(":name", userName); - servatriceDatabaseInterface->execSqlQuery(queryDelete); - } + if(smtpClient->enqueueActivationTokenMail(userName, emailAddress, token)) + { + queryDelete->bindValue(":name", userName); + servatriceDatabaseInterface->execSqlQuery(queryDelete); } } @@ -658,8 +526,7 @@ void Servatrice::statusUpdate() void Servatrice::scheduleShutdown(const QString &reason, int minutes) { shutdownReason = reason; - shutdownMinutes = minutes; - nextShutdownMessageMinutes = shutdownMinutes; + shutdownMinutes = minutes + 1; if (minutes > 0) { shutdownTimer = new QTimer; connect(shutdownTimer, SIGNAL(timeout()), this, SLOT(shutdownTimeout())); @@ -684,16 +551,15 @@ void Servatrice::incRxBytes(quint64 num) void Servatrice::shutdownTimeout() { - // Show every time counter cut in half & every minute for last 5 minutes - if (shutdownMinutes <= 5 || shutdownMinutes == nextShutdownMessageMinutes) { - if (shutdownMinutes == nextShutdownMessageMinutes) - nextShutdownMessageMinutes = shutdownMinutes / 2; + --shutdownMinutes; + if (shutdownMinutes <= 5 || isFirstShutdownMessage || shutdownMinutes % 10 == 0) { + isFirstShutdownMessage = false; SessionEvent *se; if (shutdownMinutes) { Event_ServerShutdown event; event.set_reason(shutdownReason.toStdString()); - event.set_minutes(static_cast(shutdownMinutes)); + event.set_minutes(shutdownMinutes); se = Server_ProtocolHandler::prepareSessionEvent(event); } else { Event_ConnectionClosed event; @@ -702,386 +568,59 @@ void Servatrice::shutdownTimeout() } clientsLock.lockForRead(); - for (auto &client : clients) - client->sendProtocolItem(*se); + for (int i = 0; i < clients.size(); ++i) + clients[i]->sendProtocolItem(*se); clientsLock.unlock(); delete se; - if (!shutdownMinutes) { + if (!shutdownMinutes) deleteLater(); - } } - shutdownMinutes--; } -bool Servatrice::islConnectionExists(int _serverId) const +bool Servatrice::islConnectionExists(int serverId) const { // Only call with islLock locked at least for reading - return islInterfaces.contains(_serverId); + + return islInterfaces.contains(serverId); } -void Servatrice::addIslInterface(int _serverId, IslInterface *interface) +void Servatrice::addIslInterface(int serverId, IslInterface *interface) { // Only call with islLock locked for writing - islInterfaces.insert(_serverId, interface); + + islInterfaces.insert(serverId, interface); connect(interface, SIGNAL(externalUserJoined(ServerInfo_User)), this, SLOT(externalUserJoined(ServerInfo_User))); connect(interface, SIGNAL(externalUserLeft(QString)), this, SLOT(externalUserLeft(QString))); - connect(interface, SIGNAL(externalRoomUserJoined(int, ServerInfo_User)), this, - SLOT(externalRoomUserJoined(int, ServerInfo_User))); + connect(interface, SIGNAL(externalRoomUserJoined(int, ServerInfo_User)), this, SLOT(externalRoomUserJoined(int, ServerInfo_User))); connect(interface, SIGNAL(externalRoomUserLeft(int, QString)), this, SLOT(externalRoomUserLeft(int, QString))); - connect(interface, SIGNAL(externalRoomSay(int, QString, QString)), this, - SLOT(externalRoomSay(int, QString, QString))); - connect(interface, SIGNAL(externalRoomRemoveMessages(int, QString, int)), this, - SLOT(externalRoomRemoveMessages(int, QString, int))); - connect(interface, SIGNAL(externalRoomGameListChanged(int, ServerInfo_Game)), this, - SLOT(externalRoomGameListChanged(int, ServerInfo_Game))); - connect(interface, SIGNAL(joinGameCommandReceived(Command_JoinGame, int, int, int, qint64)), this, - SLOT(externalJoinGameCommandReceived(Command_JoinGame, int, int, int, qint64))); - connect(interface, SIGNAL(gameCommandContainerReceived(CommandContainer, int, int, qint64)), this, - SLOT(externalGameCommandContainerReceived(CommandContainer, int, int, qint64))); - connect(interface, SIGNAL(responseReceived(Response, qint64)), this, - SLOT(externalResponseReceived(Response, qint64))); - connect(interface, SIGNAL(gameEventContainerReceived(GameEventContainer, qint64)), this, - SLOT(externalGameEventContainerReceived(GameEventContainer, qint64))); + connect(interface, SIGNAL(externalRoomSay(int, QString, QString)), this, SLOT(externalRoomSay(int, QString, QString))); + connect(interface, SIGNAL(externalRoomGameListChanged(int, ServerInfo_Game)), this, SLOT(externalRoomGameListChanged(int, ServerInfo_Game))); + connect(interface, SIGNAL(joinGameCommandReceived(Command_JoinGame, int, int, int, qint64)), this, SLOT(externalJoinGameCommandReceived(Command_JoinGame, int, int, int, qint64))); + connect(interface, SIGNAL(gameCommandContainerReceived(CommandContainer, int, int, qint64)), this, SLOT(externalGameCommandContainerReceived(CommandContainer, int, int, qint64))); + connect(interface, SIGNAL(responseReceived(Response, qint64)), this, SLOT(externalResponseReceived(Response, qint64))); + connect(interface, SIGNAL(gameEventContainerReceived(GameEventContainer, qint64)), this, SLOT(externalGameEventContainerReceived(GameEventContainer, qint64))); } -void Servatrice::removeIslInterface(int _serverId) +void Servatrice::removeIslInterface(int serverId) { // Only call with islLock locked for writing - // XXX we probably need to delete everything that belonged to it... <-- THIS SHOULD BE FIXED FOR ISL FUNCTIONALITY - // TO WORK COMPLETLY! - islInterfaces.remove(_serverId); + + // XXX we probably need to delete everything that belonged to it... + islInterfaces.remove(serverId); } -void Servatrice::doSendIslMessage(const IslMessage &msg, int _serverId) +void Servatrice::doSendIslMessage(const IslMessage &msg, int serverId) { QReadLocker locker(&islLock); - if (_serverId == -1) { + if (serverId == -1) { QMapIterator islIterator(islInterfaces); while (islIterator.hasNext()) islIterator.next().value()->transmitMessage(msg); } else { - IslInterface *interface = islInterfaces.value(_serverId); + IslInterface *interface = islInterfaces.value(serverId); if (interface) interface->transmitMessage(msg); } } - -// start helper functions - -int Servatrice::getMaxUserTotal() const -{ - return settingsCache->value("security/max_users_total", 500).toInt(); -} - -bool Servatrice::getMaxUserLimitEnabled() const -{ - return settingsCache->value("security/enable_max_user_limit", false).toBool(); -} - -QString Servatrice::getServerName() const -{ - return settingsCache->value("server/name", "My Cockatrice server").toString(); -} - -int Servatrice::getServerID() const -{ - return settingsCache->value("server/id", 0).toInt(); -} - -bool Servatrice::getClientIDRequiredEnabled() const -{ - return settingsCache->value("server/requireclientid", 0).toBool(); -} - -bool Servatrice::getRegOnlyServerEnabled() const -{ - return settingsCache->value("authentication/regonly", 0).toBool(); -} - -QString Servatrice::getAuthenticationMethodString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return {"sql"}; - } - return settingsCache->value("authentication/method").toString(); -} - -bool Servatrice::getStoreReplaysEnabled() const -{ - return settingsCache->value("game/store_replays", true).toBool(); -} - -int Servatrice::getMaxTcpUserLimit() const -{ - return settingsCache->value("security/max_users_tcp", 500).toInt(); -} - -int Servatrice::getMaxWebSocketUserLimit() const -{ - return settingsCache->value("security/max_users_websocket", 500).toInt(); -} - -bool Servatrice::getRegistrationEnabled() const -{ - return settingsCache->value("registration/enabled", false).toBool(); -} - -bool Servatrice::getRequireEmailForRegistrationEnabled() const -{ - return settingsCache->value("registration/requireemail", true).toBool(); -} - -bool Servatrice::getRequireEmailActivationEnabled() const -{ - return settingsCache->value("registration/requireemailactivation", true).toBool(); -} - -QString Servatrice::getRequiredFeatures() const -{ - return settingsCache->value("server/requiredfeatures", "").toString(); -} - -QString Servatrice::getDBTypeString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return {"mysql"}; - } - return settingsCache->value("database/type").toString(); -} - -QString Servatrice::getDBPrefixString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return {"cockatrice"}; - } - return settingsCache->value("database/prefix").toString(); -} - -QString Servatrice::getDBHostNameString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return QUrl(QProcessEnvironment::systemEnvironment().value("DATABASE_URL")).host(); - } - return settingsCache->value("database/hostname").toString(); -} - -QString Servatrice::getDBDatabaseNameString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - QString path = QUrl(QProcessEnvironment::systemEnvironment().value("DATABASE_URL")).path(); - return path.right(path.length() - 1); - } - return settingsCache->value("database/database").toString(); -} - -QString Servatrice::getDBUserNameString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return QUrl(QProcessEnvironment::systemEnvironment().value("DATABASE_URL")).userName(); - } - return settingsCache->value("database/user").toString(); -} - -QString Servatrice::getDBPasswordString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return QUrl(QProcessEnvironment::systemEnvironment().value("DATABASE_URL")).password(); - } - return settingsCache->value("database/password").toString(); -} - -QString Servatrice::getRoomsMethodString() const -{ - if (QProcessEnvironment::systemEnvironment().contains("DATABASE_URL")) { - return {"sql"}; - } - return settingsCache->value("rooms/method").toString(); -} - -int Servatrice::getMaxGameInactivityTime() const -{ - return settingsCache->value("game/max_game_inactivity_time", 120).toInt(); -} - -int Servatrice::getMaxPlayerInactivityTime() const -{ - return settingsCache->value("server/max_player_inactivity_time", 15).toInt(); -} - -int Servatrice::getClientKeepAlive() const -{ - return settingsCache->value("server/clientkeepalive", 1).toInt(); -} - -int Servatrice::getMaxUsersPerAddress() const -{ - return settingsCache->value("security/max_users_per_address", 4).toInt(); -} - -int Servatrice::getMessageCountingInterval() const -{ - return settingsCache->value("security/message_counting_interval", 10).toInt(); -} - -int Servatrice::getMaxMessageCountPerInterval() const -{ - return settingsCache->value("security/max_message_count_per_interval", 15).toInt(); -} - -int Servatrice::getMaxMessageSizePerInterval() const -{ - return settingsCache->value("security/max_message_size_per_interval", 1000).toInt(); -} - -int Servatrice::getMaxGamesPerUser() const -{ - return settingsCache->value("security/max_games_per_user", 5).toInt(); -} - -int Servatrice::getCommandCountingInterval() const -{ - return settingsCache->value("security/command_counting_interval", 10).toInt(); -} - -int Servatrice::getMaxCommandCountPerInterval() const -{ - return settingsCache->value("security/max_command_count_per_interval", 20).toInt(); -} - -int Servatrice::getServerStatusUpdateTime() const -{ - return settingsCache->value("server/statusupdate", 15000).toInt(); -} - -int Servatrice::getNumberOfTCPPools() const -{ - return settingsCache->value("server/number_pools", 1).toInt(); -} - -bool Servatrice::permitCreateGameAsJudge() const -{ - return settingsCache->value("game/allow_create_as_judge", false).toBool(); -} - -QHostAddress Servatrice::getServerTCPHost() const -{ - QString host = settingsCache->value("server/host", "any").toString(); - if (host == "any") - return QHostAddress::Any; - else - return QHostAddress(host); -} - -int Servatrice::getServerTCPPort() const -{ - return settingsCache->value("server/port", 4747).toInt(); -} - -int Servatrice::getNumberOfWebSocketPools() const -{ - return settingsCache->value("server/websocket_number_pools", 1).toInt(); -} - -QHostAddress Servatrice::getServerWebSocketHost() const -{ - QString host = settingsCache->value("server/websocket_host", "any").toString(); - if (host == "any") - return QHostAddress::Any; - else - return QHostAddress(host); -} - -int Servatrice::getServerWebSocketPort() const -{ - if (QProcessEnvironment::systemEnvironment().contains("PORT")) { - return QProcessEnvironment::systemEnvironment().value("PORT").toInt(); - } - return settingsCache->value("server/websocket_port", 4748).toInt(); -} - -bool Servatrice::getISLNetworkEnabled() const -{ - return settingsCache->value("servernetwork/active", false).toBool(); -} - -QString Servatrice::getISLNetworkSSLCertFile() const -{ - return settingsCache->value("servernetwork/ssl_cert").toString(); -} - -QString Servatrice::getISLNetworkSSLKeyFile() const -{ - return settingsCache->value("servernetwork/ssl_key").toString(); -} - -int Servatrice::getISLNetworkPort() const -{ - return settingsCache->value("servernetwork/port", 14747).toInt(); -} - -int Servatrice::getIdleClientTimeout() const -{ - return settingsCache->value("server/idleclienttimeout", 3600).toInt(); -} - -bool Servatrice::getEnableLogQuery() const -{ - return settingsCache->value("logging/enablelogquery", false).toBool(); -} - -int Servatrice::getMaxAccountsPerEmail() const -{ - return settingsCache->value("registration/maxaccountsperemail", 0).toInt(); -} - -bool Servatrice::getEnableInternalSMTPClient() const -{ - return settingsCache->value("smtp/enableinternalsmtpclient", true).toBool(); -} - -bool Servatrice::getEnableForgotPassword() const -{ - return settingsCache->value("forgotpassword/enable", false).toBool(); -} - -int Servatrice::getForgotPasswordTokenLife() const -{ - return settingsCache->value("forgotpassword/tokenlife", 60).toInt(); -} - -bool Servatrice::getEnableForgotPasswordChallenge() const -{ - return settingsCache->value("forgotpassword/enablechallenge", false).toBool(); -} - -QString Servatrice::getEmailBlackList() const -{ - return settingsCache->value("registration/emailproviderblacklist").toString(); -} - -QString Servatrice::getEmailWhiteList() const -{ - return settingsCache->value("registration/emailproviderwhitelist").toString(); -} - -bool Servatrice::getEnableAudit() const -{ - return settingsCache->value("audit/enable_audit", true).toBool(); -} - -bool Servatrice::getEnableRegistrationAudit() const -{ - return settingsCache->value("audit/enable_registration_audit", true).toBool(); -} - -bool Servatrice::getEnableForgotPasswordAudit() const -{ - return settingsCache->value("audit/enable_forgotpassword_audit", true).toBool(); -} - -int Servatrice::getMinPasswordLength() const -{ - return settingsCache->value("users/minpasswordlength", 6).toInt(); -} diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 62fb382cb..e89cfdef9 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -20,17 +20,16 @@ #ifndef SERVATRICE_H #define SERVATRICE_H -#include -#include +#include #include -#include -#include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include "server.h" + Q_DECLARE_METATYPE(QSqlDatabase) @@ -41,72 +40,44 @@ class GameReplay; class Servatrice; class Servatrice_ConnectionPool; class Servatrice_DatabaseInterface; -class AbstractServerSocketInterface; +class ServerSocketInterface; class IslInterface; class FeatureSet; -class Servatrice_GameServer : public QTcpServer -{ +class Servatrice_GameServer : public QTcpServer { Q_OBJECT private: Servatrice *server; QList connectionPools; - public: - Servatrice_GameServer(Servatrice *_server, - int _numberPools, - const QSqlDatabase &_sqlDatabase, - QObject *parent = nullptr); - ~Servatrice_GameServer() override; - + Servatrice_GameServer(Servatrice *_server, int _numberPools, const QSqlDatabase &_sqlDatabase, QObject *parent = 0); + ~Servatrice_GameServer(); protected: - void incomingConnection(qintptr socketDescriptor) override; - Servatrice_ConnectionPool *findLeastUsedConnectionPool(); +#if QT_VERSION < 0x050000 + void incomingConnection(int socketDescriptor); +#else + void incomingConnection(qintptr socketDescriptor); +#endif }; -class Servatrice_WebsocketGameServer : public QWebSocketServer -{ - Q_OBJECT -private: - Servatrice *server; - QList connectionPools; - -public: - Servatrice_WebsocketGameServer(Servatrice *_server, - int _numberPools, - const QSqlDatabase &_sqlDatabase, - QObject *parent = nullptr); - ~Servatrice_WebsocketGameServer() override; - -protected: - Servatrice_ConnectionPool *findLeastUsedConnectionPool(); -protected slots: - void onNewConnection(); -}; - -class Servatrice_IslServer : public QTcpServer -{ +class Servatrice_IslServer : public QTcpServer { Q_OBJECT private: Servatrice *server; QSslCertificate cert; QSslKey privateKey; - public: - Servatrice_IslServer(Servatrice *_server, - const QSslCertificate &_cert, - QSslKey _privateKey, - QObject *parent = nullptr) - : QTcpServer(parent), server(_server), cert(_cert), privateKey(std::move(_privateKey)) - { - } - + Servatrice_IslServer(Servatrice *_server, const QSslCertificate &_cert, const QSslKey &_privateKey, QObject *parent = 0) + : QTcpServer(parent), server(_server), cert(_cert), privateKey(_privateKey) { } protected: - void incomingConnection(qintptr socketDescriptor) override; +#if QT_VERSION < 0x050000 + void incomingConnection(int socketDescriptor); +#else + void incomingConnection(qintptr socketDescriptor); +#endif }; -class ServerProperties -{ +class ServerProperties { public: int id; QSslCertificate cert; @@ -115,50 +86,32 @@ public: int gamePort; int controlPort; - ServerProperties(int _id, - const QSslCertificate &_cert, - QString _hostname, - const QHostAddress &_address, - int _gamePort, - int _controlPort) - : id(_id), cert(_cert), hostname(std::move(_hostname)), address(_address), gamePort(_gamePort), - controlPort(_controlPort) - { - } + ServerProperties(int _id, const QSslCertificate &_cert, const QString &_hostname, const QHostAddress &_address, int _gamePort, int _controlPort) + : id(_id), cert(_cert), hostname(_hostname), address(_address), gamePort(_gamePort), controlPort(_controlPort) { } }; class Servatrice : public Server { Q_OBJECT public: - enum AuthenticationMethod - { - AuthenticationNone, - AuthenticationSql, - AuthenticationPassword - }; + enum AuthenticationMethod { AuthenticationNone, AuthenticationSql, AuthenticationPassword }; private slots: void statusUpdate(); void shutdownTimeout(); - protected: - void doSendIslMessage(const IslMessage &msg, int _serverId) override; - + void doSendIslMessage(const IslMessage &msg, int serverId); private: - enum DatabaseType - { - DatabaseNone, - DatabaseMySql - }; + enum DatabaseType { DatabaseNone, DatabaseMySql }; AuthenticationMethod authenticationMethod; DatabaseType databaseType; QTimer *pingClock, *statusUpdateClock; Servatrice_GameServer *gameServer; - Servatrice_WebsocketGameServer *websocketGameServer; Servatrice_IslServer *islServer; + QString serverName; mutable QMutex loginMessageMutex; QString loginMessage; QString dbPrefix; + QString requiredFeatures; QMap serverRequiredFeatureList; QString officialWarnings; Servatrice_DatabaseInterface *servatriceDatabaseInterface; @@ -166,118 +119,57 @@ private: int uptime; QMutex txBytesMutex, rxBytesMutex; quint64 txBytes, rxBytes; + int maxGameInactivityTime, maxPlayerInactivityTime; + int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval, pingClockInterval; QString shutdownReason; int shutdownMinutes; - int nextShutdownMessageMinutes; QTimer *shutdownTimer; + bool isFirstShutdownMessage, clientIdRequired, regServerOnly; mutable QMutex serverListMutex; QList serverList; void updateServerList(); QMap islInterfaces; - - QString getDBPrefixString() const; - QString getDBHostNameString() const; - QString getDBDatabaseNameString() const; - QString getDBUserNameString() const; - QString getDBPasswordString() const; - QString getRoomsMethodString() const; - QString getISLNetworkSSLCertFile() const; - QString getISLNetworkSSLKeyFile() const; - int getServerStatusUpdateTime() const; - int getNumberOfTCPPools() const; - int getServerTCPPort() const; - int getNumberOfWebSocketPools() const; - int getServerWebSocketPort() const; - int getISLNetworkPort() const; - bool getISLNetworkEnabled() const; - bool getEnableInternalSMTPClient() const; - QHostAddress getServerTCPHost() const; - QHostAddress getServerWebSocketHost() const; - public slots: void scheduleShutdown(const QString &reason, int minutes); void updateLoginMessage(); - void setRequiredFeatures(const QString &featureList); - public: - explicit Servatrice(QObject *parent = nullptr); - ~Servatrice() override; + Servatrice(QObject *parent = 0); + ~Servatrice(); bool initServer(); - QMap getServerRequiredFeatureList() const override - { - return serverRequiredFeatureList; - } - QString getServerName() const; - QString getLoginMessage() const override - { - QMutexLocker locker(&loginMessageMutex); - return loginMessage; - } - QString getRequiredFeatures() const override; - QString getAuthenticationMethodString() const; - QString getDBTypeString() const; - QString getDbPrefix() const - { - return dbPrefix; - } - QString getEmailBlackList() const; - QString getEmailWhiteList() const; - AuthenticationMethod getAuthenticationMethod() const - { - return authenticationMethod; - } - bool permitUnregisteredUsers() const override - { - return authenticationMethod != AuthenticationNone; - } - bool getGameShouldPing() const override - { - return true; - } - bool getClientIDRequiredEnabled() const override; - bool getRegOnlyServerEnabled() const override; - bool getMaxUserLimitEnabled() const override; - bool getStoreReplaysEnabled() const override; - bool getRegistrationEnabled() const; - bool getRequireEmailForRegistrationEnabled() const; - bool getRequireEmailActivationEnabled() const; - bool getEnableLogQuery() const override; - bool getEnableForgotPassword() const; - bool getEnableForgotPasswordChallenge() const; - bool getEnableAudit() const; - bool getEnableRegistrationAudit() const; - bool getEnableForgotPasswordAudit() const; - int getMinPasswordLength() const; - int getIdleClientTimeout() const override; - int getServerID() const override; - int getMaxGameInactivityTime() const override; - int getMaxPlayerInactivityTime() const override; - int getClientKeepAlive() const override; - int getMaxUsersPerAddress() const; - int getMessageCountingInterval() const override; - int getMaxMessageCountPerInterval() const override; - int getMaxMessageSizePerInterval() const override; - int getMaxGamesPerUser() const override; - int getCommandCountingInterval() const override; - int getMaxCommandCountPerInterval() const override; - int getMaxUserTotal() const override; - bool permitCreateGameAsJudge() const override; - int getMaxTcpUserLimit() const; - int getMaxWebSocketUserLimit() const; + QMap getServerRequiredFeatureList() const { return serverRequiredFeatureList; } + QString getOfficialWarningsList() const { return officialWarnings; } + QString getServerName() const { return serverName; } + QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; } + QString getRequiredFeatures() const { return requiredFeatures; } + bool permitUnregisteredUsers() const { return authenticationMethod != AuthenticationNone; } + bool getGameShouldPing() const { return true; } + bool getClientIdRequired() const { return clientIdRequired; } + bool getRegOnlyServer() const { return regServerOnly; } + int getPingClockInterval() const { return pingClockInterval; } + int getMaxGameInactivityTime() const { return maxGameInactivityTime; } + int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; } + int getMaxUsersPerAddress() const { return maxUsersPerAddress; } + int getMessageCountingInterval() const { return messageCountingInterval; } + int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; } + int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; } + int getMaxGamesPerUser() const { return maxGamesPerUser; } + int getCommandCountingInterval() const { return commandCountingInterval; } + int getMaxCommandCountPerInterval() const { return maxCommandCountPerInterval; } + AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; } + QString getDbPrefix() const { return dbPrefix; } + int getServerId() const { return serverId; } int getUsersWithAddress(const QHostAddress &address) const; - int getMaxAccountsPerEmail() const; - int getForgotPasswordTokenLife() const; - QList getUsersWithAddressAsList(const QHostAddress &address) const; + QList getUsersWithAddressAsList(const QHostAddress &address) const; void incTxBytes(quint64 num); void incRxBytes(quint64 num); void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface); - bool islConnectionExists(int _serverId) const; - void addIslInterface(int _serverId, IslInterface *interface); - void removeIslInterface(int _serverId); + bool islConnectionExists(int serverId) const; + void addIslInterface(int serverId, IslInterface *interface); + void removeIslInterface(int serverId); QReadWriteLock islLock; QList getServerList() const; diff --git a/servatrice/src/servatrice_connection_pool.cpp b/servatrice/src/servatrice_connection_pool.cpp index 3edb4da27..a2f849be4 100644 --- a/servatrice/src/servatrice_connection_pool.cpp +++ b/servatrice/src/servatrice_connection_pool.cpp @@ -1,16 +1,15 @@ #include "servatrice_connection_pool.h" - #include "servatrice_database_interface.h" - #include Servatrice_ConnectionPool::Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface) - : databaseInterface(_databaseInterface), threaded(false), clientCount(0) + : databaseInterface(_databaseInterface), + clientCount(0) { } Servatrice_ConnectionPool::~Servatrice_ConnectionPool() { - delete databaseInterface; - thread()->quit(); + delete databaseInterface; + thread()->quit(); } diff --git a/servatrice/src/servatrice_connection_pool.h b/servatrice/src/servatrice_connection_pool.h index 7479122cf..f1bbc26f7 100644 --- a/servatrice/src/servatrice_connection_pool.h +++ b/servatrice/src/servatrice_connection_pool.h @@ -1,46 +1,29 @@ #ifndef SERVATRICE_CONNECTION_POOL_H #define SERVATRICE_CONNECTION_POOL_H +#include #include #include -#include class Servatrice_DatabaseInterface; -class Servatrice_ConnectionPool : public QObject -{ - Q_OBJECT +class Servatrice_ConnectionPool : public QObject { + Q_OBJECT private: - Servatrice_DatabaseInterface *databaseInterface; - bool threaded; - mutable QMutex clientCountMutex; - int clientCount; - + Servatrice_DatabaseInterface *databaseInterface; + bool threaded; + mutable QMutex clientCountMutex; + int clientCount; public: - explicit Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface); - ~Servatrice_ConnectionPool() override; - - Servatrice_DatabaseInterface *getDatabaseInterface() const - { - return databaseInterface; - } - - int getClientCount() const - { - QMutexLocker locker(&clientCountMutex); - return clientCount; - } - void addClient() - { - QMutexLocker locker(&clientCountMutex); - ++clientCount; - } + Servatrice_ConnectionPool(Servatrice_DatabaseInterface *_databaseInterface); + ~Servatrice_ConnectionPool(); + + Servatrice_DatabaseInterface *getDatabaseInterface() const { return databaseInterface; } + + int getClientCount() const { QMutexLocker locker(&clientCountMutex); return clientCount; } + void addClient() { QMutexLocker locker(&clientCountMutex); ++clientCount; } public slots: - void removeClient() - { - QMutexLocker locker(&clientCountMutex); - --clientCount; - } + void removeClient() { QMutexLocker locker(&clientCountMutex); --clientCount; } }; #endif diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index bce3542e8..e1f53ae0d 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -1,23 +1,20 @@ -#include "servatrice_database_interface.h" - #include "servatrice.h" +#include "servatrice_database_interface.h" +#include "passwordhasher.h" #include "serversocketinterface.h" #include "settingscache.h" - -#include -#include +#include "decklist.h" +#include "pb/game_replay.pb.h" #include -#include #include #include -#include -#include -#include - -inline Q_LOGGING_CATEGORY(DatabaseInterfaceLog, "database_interface"); +#include +#include Servatrice_DatabaseInterface::Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server) - : instanceId(_instanceId), sqlDatabase(QSqlDatabase()), server(_server) + : instanceId(_instanceId), + sqlDatabase(QSqlDatabase()), + server(_server) { } @@ -38,10 +35,8 @@ void Servatrice_DatabaseInterface::initDatabase(const QSqlDatabase &_sqlDatabase } } -bool Servatrice_DatabaseInterface::initDatabase(const QString &type, - const QString &hostName, - const QString &databaseName, - const QString &userName, +bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QString &hostName, + const QString &databaseName, const QString &userName, const QString &password) { sqlDatabase = QSqlDatabase::addDatabase(type, "main"); @@ -59,38 +54,31 @@ bool Servatrice_DatabaseInterface::openDatabase() sqlDatabase.close(); const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qCDebug(DatabaseInterfaceLog).noquote() << poolStr << "Opening database..."; + qDebug() << QString("[%1] Opening database...").arg(poolStr); if (!sqlDatabase.open()) { - qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database:" << sqlDatabase.lastError().text(); + qCritical() << QString("[%1] Error opening database: %2").arg(poolStr).arg(sqlDatabase.lastError().text()); return false; } QSqlQuery *versionQuery = prepareQuery("select version from {prefix}_schema_version limit 1"); if (!execSqlQuery(versionQuery)) { - qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: unable to load database schema version" - << "(hint: ensure the cockatrice_schema_version exists)"; + qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the cockatrice_schema_version exists)").arg(poolStr); return false; } if (versionQuery->next()) { const int dbversion = versionQuery->value(0).toInt(); const int expectedversion = DATABASE_SCHEMA_VERSION; - if (dbversion < expectedversion) { - qCCritical(DatabaseInterfaceLog) << poolStr - << "Error opening database: the database schema version is too old, you " - "need to run the migrations to update it from version" - << dbversion << "to version" << expectedversion; + if(dbversion < expectedversion) + { + qCritical() << QString("[%1] Error opening database: the database schema version is too old, you need to run the migrations to update it from version %2 to version %3").arg(poolStr).arg(dbversion).arg(expectedversion); return false; - } else if (dbversion > expectedversion) { - qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: the database schema version" - << dbversion << "is too new, you need to update servatrice" - << "(this servatrice actually uses version" << expectedversion << ")"; + } else if(dbversion > expectedversion) { + qCritical() << QString("[%1] Error opening database: the database schema version %2 is too new, you need to update servatrice (this servatrice actually uses version %3)").arg(poolStr).arg(dbversion).arg(expectedversion); return false; } } else { - qCCritical(DatabaseInterfaceLog) << poolStr - << "Error opening database: unable to load database schema version (hint: " - "ensure the cockatrice_schema_version contains a single record)"; + qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the cockatrice_schema_version contains a single record)").arg(poolStr); return false; } @@ -102,35 +90,22 @@ bool Servatrice_DatabaseInterface::openDatabase() bool Servatrice_DatabaseInterface::checkSql() { - if (!sqlDatabase.isValid()) { + if (!sqlDatabase.isValid()) return false; - } - auto query = QSqlQuery(sqlDatabase); - if (query.exec("select 1") && !query.isActive()) { + if (!sqlDatabase.exec("select 1").isActive()) return openDatabase(); - } - - if (query.lastError().isValid()) { - const auto &poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query.lastError().text(); - - sqlDatabase.close(); - return openDatabase(); - } - return true; } -QSqlQuery *Servatrice_DatabaseInterface::prepareQuery(const QString &queryText) +QSqlQuery * Servatrice_DatabaseInterface::prepareQuery(const QString &queryText) { - if (preparedStatements.contains(queryText)) { + if(preparedStatements.contains(queryText)) return preparedStatements.value(queryText); - } QString prefixedQueryText = queryText; prefixedQueryText.replace("{prefix}", server->getDbPrefix()); - auto *query = new QSqlQuery(sqlDatabase); + QSqlQuery * query = new QSqlQuery(sqlDatabase); query->prepare(prefixedQueryText); preparedStatements.insert(queryText, query); @@ -142,16 +117,14 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) if (query->exec()) return true; const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); - qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query->lastError().text(); - sqlDatabase.close(); - openDatabase(); + qCritical() << QString("[%1] Error executing query: %2").arg(poolStr).arg(query->lastError().text()); return false; } -bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString &error) +bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString & error) { int minNameLength = settingsCache->value("users/minnamelength", 6).toInt(); - if (minNameLength < 1) + if(minNameLength < 1) minNameLength = 1; int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool(); @@ -159,30 +132,7 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString bool allowNumerics = settingsCache->value("users/allownumerics", true).toBool(); bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); - QString disallowedWordsStr = settingsCache->value("users/disallowedwords", "").toString(); - QStringList disallowedWords = disallowedWordsStr.split(",", Qt::SkipEmptyParts); - disallowedWords.removeDuplicates(); - QVariant displayDisallowedWords = settingsCache->value("users/displaydisallowedwords"); - QString disallowedRegExpStr; - if (displayDisallowedWords.isValid()) { - disallowedWordsStr = displayDisallowedWords.toString().trimmed(); - if (!disallowedWordsStr.isEmpty()) { - disallowedWordsStr.prepend("\n"); - } - } else { - disallowedRegExpStr = settingsCache->value("users/disallowedregexp", "").toString(); - } - - error = QString("%1|%2|%3|%4|%5|%6|%7|%8|%9") - .arg(minNameLength) - .arg(maxNameLength) - .arg(allowLowercase) - .arg(allowUppercase) - .arg(allowNumerics) - .arg(allowPunctuationPrefix) - .arg(allowedPunctuation) - .arg(disallowedWordsStr) - .arg(disallowedRegExpStr); + error = QString("%1|%2|%3|%4|%5|%6|%7").arg(minNameLength).arg(maxNameLength).arg(allowLowercase).arg(allowUppercase).arg(allowNumerics).arg(allowPunctuationPrefix).arg(allowedPunctuation); if (user.length() < minNameLength || user.length() > maxNameLength) return false; @@ -190,58 +140,35 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString if (!allowPunctuationPrefix && allowedPunctuation.contains(user.at(0))) return false; - for (const QString &word : disallowedWords) { - if (user.contains(word, Qt::CaseInsensitive)) - return false; - } - - for (const QRegularExpression ®Exp : settingsCache->disallowedRegExp) { - if (regExp.match(user).hasMatch()) - return false; - } - - QString regEx("\\A["); + QString regEx("["); if (allowLowercase) regEx.append("a-z"); if (allowUppercase) regEx.append("A-Z"); - if (allowNumerics) + if(allowNumerics) regEx.append("0-9"); - regEx.append(QRegularExpression::escape(allowedPunctuation)); - regEx.append("]+\\z"); + regEx.append(QRegExp::escape(allowedPunctuation)); + regEx.append("]+"); - QRegularExpression re = QRegularExpression(regEx); - return re.match(user).hasMatch(); + static QRegExp re = QRegExp(regEx); + return re.exactMatch(user); } -bool Servatrice_DatabaseInterface::registerUser(const QString &userName, - const QString &realName, - const QString &password, - bool passwordNeedsHash, - const QString &emailAddress, - const QString &country, - bool active) +bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active) { if (!checkSql()) return false; - QString passwordSha512; - if (passwordNeedsHash) { - passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); - } else { - passwordSha512 = password; - } - QString token = active ? QString() : PasswordHasher::generateActivationToken(); + QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); + token = active ? QString() : PasswordHasher::generateActivationToken(); - QSqlQuery *query = - prepareQuery("insert into {prefix}_users " - "(name, realname, password_sha512, email, country, registrationDate, active, token, " - "admin, avatar_bmp, clientid, privlevel, privlevelStartDate, privlevelEndDate) " - "values " - "(:userName, :realName, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, " - ":token, 0, '', '', 'NONE', UTC_TIMESTAMP(), UTC_TIMESTAMP())"); + QSqlQuery *query = prepareQuery("insert into {prefix}_users " + "(name, realname, gender, password_sha512, email, country, registrationDate, active, token) " + "values " + "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)"); query->bindValue(":userName", userName); query->bindValue(":realName", realName); + query->bindValue(":gender", getGenderChar(gender)); query->bindValue(":password_sha512", passwordSha512); query->bindValue(":email", emailAddress); query->bindValue(":country", country); @@ -249,8 +176,7 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName, query->bindValue(":token", token); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to insert user: " << query->lastError() - << " sql: " << query->lastQuery(); + qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery(); return false; } @@ -262,28 +188,26 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q if (!checkSql()) return false; - QSqlQuery *activateQuery = - prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token"); + QSqlQuery *activateQuery = prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token"); activateQuery->bindValue(":username", userName); activateQuery->bindValue(":token", token); if (!execSqlQuery(activateQuery)) { - qCWarning(DatabaseInterfaceLog) << "Account activation failed: SQL error." << activateQuery->lastError() - << " sql: " << activateQuery->lastQuery(); + qDebug() << "Account activation failed: SQL error." << activateQuery->lastError()<< " sql: " << activateQuery->lastQuery(); return false; } if (activateQuery->next()) { const QString name = activateQuery->value(0).toString(); // redundant check - if (name == userName) { + if(name == userName) + { QSqlQuery *query = prepareQuery("update {prefix}_users set active=1 where name = :userName"); query->bindValue(":userName", userName); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) - << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery(); + qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery(); return false; } @@ -293,113 +217,107 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q return false; } -AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, - const QString &user, - const QString &password, - const QString &clientId, - QString &reasonStr, - int &banSecondsLeft, - bool passwordNeedsHash) +QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &gender) +{ + switch (gender) { + case ServerInfo_User_Gender_GenderUnknown: + return QChar('u'); + case ServerInfo_User_Gender_Male: + return QChar('m'); + case ServerInfo_User_Gender_Female: + return QChar('f'); + default: + return QChar('u'); + } +} + +AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &banSecondsLeft) { switch (server->getAuthenticationMethod()) { - case Servatrice::AuthenticationNone: - return UnknownUser; - case Servatrice::AuthenticationPassword: { - QString configPassword = settingsCache->value("authentication/password").toString(); - if (configPassword == password) - return PasswordRight; + case Servatrice::AuthenticationNone: return UnknownUser; + case Servatrice::AuthenticationPassword: { + QString configPassword = settingsCache->value("authentication/password").toString(); + if (configPassword == password) + return PasswordRight; + return NotLoggedIn; + } + case Servatrice::AuthenticationSql: { + if (!checkSql()) + return UnknownUser; + + if (!usernameIsValid(user, reasonStr)) + return UsernameInvalid; + + if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft)) + return UserIsBanned; + + QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); + passwordQuery->bindValue(":name", user); + if (!execSqlQuery(passwordQuery)) { + qDebug("Login denied: SQL error"); return NotLoggedIn; } - case Servatrice::AuthenticationSql: { - if (!checkSql()) - return UnknownUser; - if (!usernameIsValid(user, reasonStr)) - return UsernameInvalid; - - if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft)) - return UserIsBanned; - - QSqlQuery *passwordQuery = - prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); - passwordQuery->bindValue(":name", user); - if (!execSqlQuery(passwordQuery)) { - qCWarning(DatabaseInterfaceLog) << "Login denied: SQL error"; + if (passwordQuery->next()) { + const QString correctPassword = passwordQuery->value(0).toString(); + const bool userIsActive = passwordQuery->value(1).toBool(); + if(!userIsActive) { + qDebug("Login denied: user not active"); + return UserIsInactive; + } + if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) { + qDebug("Login accepted: password right"); + return PasswordRight; + } else { + qDebug("Login denied: password wrong"); return NotLoggedIn; } - - if (passwordQuery->next()) { - const QString correctPasswordSha512 = passwordQuery->value(0).toString(); - const bool userIsActive = passwordQuery->value(1).toBool(); - if (!userIsActive) { - qCWarning(DatabaseInterfaceLog) << "Login denied: user not active"; - return UserIsInactive; - } - QString hashedPassword; - if (passwordNeedsHash) { - hashedPassword = PasswordHasher::computeHash(password, correctPasswordSha512.left(16)); - } else { - hashedPassword = password; - } - if (correctPasswordSha512 == hashedPassword) { - qCDebug(DatabaseInterfaceLog) << "Login accepted: password right"; - return PasswordRight; - } else { - qCDebug(DatabaseInterfaceLog) << "Login denied: password wrong"; - return NotLoggedIn; - } - } else { - qCDebug(DatabaseInterfaceLog) << "Login accepted: unknown user"; - return UnknownUser; - } + } else { + qDebug("Login accepted: unknown user"); + return UnknownUser; } } + } return UnknownUser; } -bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, - const QString &userName, - const QString &clientId, - QString &banReason, - int &banSecondsRemaining) +bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining) { if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql) return false; if (!checkSql()) { - qCWarning(DatabaseInterfaceLog) << "Failed to check if user is banned. Database invalid."; + qDebug("Failed to check if user is banned. Database invalid."); return false; } - return checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || - checkUserIsNameBanned(userName, banReason, banSecondsRemaining) || - checkUserIsIdBanned(clientId, banReason, banSecondsRemaining); + return + checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || checkUserIsNameBanned(userName, banReason, banSecondsRemaining) || checkUserIsIdBanned(clientId, banReason, banSecondsRemaining); + } -bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, - QString &banReason, - int &banSecondsRemaining) +bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining) { if (clientId.isEmpty()) return false; - QSqlQuery *idBanQuery = - prepareQuery("select" - " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," - " b.minutes <=> 0," - " b.visible_reason" - " from {prefix}_bans b" - " where" - " b.time_from = (select max(c.time_from)" - " from {prefix}_bans c" - " where c.clientid = :id)" - " and b.clientid = :id2"); + QSqlQuery *idBanQuery = prepareQuery( + "select" + " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," + " b.minutes <=> 0," + " b.visible_reason" + " from {prefix}_bans b" + " where" + " b.time_from = (select max(c.time_from)" + " from {prefix}_bans c" + " where c.clientid = :id)" + " and b.clientid = :id2"); idBanQuery->bindValue(":id", clientId); idBanQuery->bindValue(":id2", clientId); if (!execSqlQuery(idBanQuery)) { - qCWarning(DatabaseInterfaceLog) << "Id ban check failed: SQL error." << idBanQuery->lastError(); + qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError(); return false; } @@ -409,25 +327,21 @@ bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, if ((secondsLeft > 0) || permanentBan) { banReason = idBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qCDebug(DatabaseInterfaceLog) << "User is banned by client id" << clientId; + qDebug() << "User is banned by client id" << clientId; return true; } } return false; } -bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, - QString &banReason, - int &banSecondsRemaining) + +bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining) { - QSqlQuery *nameBanQuery = - prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes " - "<=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from " - "{prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); + QSqlQuery *nameBanQuery = prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); nameBanQuery->bindValue(":name1", userName); nameBanQuery->bindValue(":name2", userName); if (!execSqlQuery(nameBanQuery)) { - qCWarning(DatabaseInterfaceLog) << "Name ban check failed: SQL error" << nameBanQuery->lastError(); + qDebug() << "Name ban check failed: SQL error" << nameBanQuery->lastError(); return false; } @@ -437,33 +351,31 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName if ((secondsLeft > 0) || permanentBan) { banReason = nameBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qCDebug(DatabaseInterfaceLog) << "Username" << userName << "is banned by name"; + qDebug() << "Username" << userName << "is banned by name"; return true; } } return false; } -bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, - QString &banReason, - int &banSecondsRemaining) +bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining) { - QSqlQuery *ipBanQuery = - prepareQuery("select" - " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," - " b.minutes <=> 0," - " b.visible_reason" - " from {prefix}_bans b" - " where" - " b.time_from = (select max(c.time_from)" - " from {prefix}_bans c" - " where c.ip_address = :address)" - " and b.ip_address = :address2"); + QSqlQuery *ipBanQuery = prepareQuery( + "select" + " timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute))," + " b.minutes <=> 0," + " b.visible_reason" + " from {prefix}_bans b" + " where" + " b.time_from = (select max(c.time_from)" + " from {prefix}_bans c" + " where c.ip_address = :address)" + " and b.ip_address = :address2"); ipBanQuery->bindValue(":address", ipAddress); ipBanQuery->bindValue(":address2", ipAddress); if (!execSqlQuery(ipBanQuery)) { - qCWarning(DatabaseInterfaceLog) << "IP ban check failed: SQL error." << ipBanQuery->lastError(); + qDebug() << "IP ban check failed: SQL error." << ipBanQuery->lastError(); return false; } @@ -473,7 +385,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, if ((secondsLeft > 0) || permanentBan) { banReason = ipBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; - qCDebug(DatabaseInterfaceLog) << "User is banned by address" << ipAddress; + qDebug() << "User is banned by address" << ipAddress; return true; } } @@ -508,28 +420,6 @@ bool Servatrice_DatabaseInterface::userExists(const QString &user) return false; } -QString Servatrice_DatabaseInterface::getUserSalt(const QString &user) -{ - if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { - checkSql(); - - QSqlQuery *query = - prepareQuery("SELECT SUBSTRING(password_sha512, 1, 16) FROM {prefix}_users WHERE name = :name"); - - query->bindValue(":name", user); - if (!execSqlQuery(query)) { - return {}; - } - - if (!query->next()) { - return {}; - } - - return query->value(0).toString(); - } - return {}; -} - int Servatrice_DatabaseInterface::getUserIdInDB(const QString &name) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { @@ -555,8 +445,7 @@ bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); - QSqlQuery *query = - prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2"); + QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); if (!execSqlQuery(query)) @@ -575,8 +464,7 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); - QSqlQuery *query = - prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2"); + QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); if (!execSqlQuery(query)) @@ -594,54 +482,42 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer const int is_admin = query->value(2).toInt(); int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; - if (is_admin & 1) + if (is_admin == 1) userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; - else if (is_admin & 2) + else if (is_admin == 2) userLevel |= ServerInfo_User::IsModerator; - - if (is_admin & 4) - userLevel |= ServerInfo_User::IsJudge; - result.set_user_level(userLevel); const QString country = query->value(3).toString(); if (!country.isEmpty()) result.set_country(country.toStdString()); - const QString privlevel = query->value(4).toString(); - if (!privlevel.isEmpty()) - result.set_privlevel(privlevel.toStdString()); - - const auto &pawn_left_override = query->value(5).toString(); - const auto &pawn_right_override = query->value(6).toString(); - if (!pawn_left_override.isEmpty()) { - result.mutable_pawn_colors()->set_left_side(pawn_left_override.toStdString()); - } - if (!pawn_right_override.isEmpty()) { - result.mutable_pawn_colors()->set_right_side(pawn_right_override.toStdString()); - } - if (complete) { - const QString realName = query->value(7).toString(); + const QString genderStr = query->value(4).toString(); + if (genderStr == "m") + result.set_gender(ServerInfo_User::Male); + else if (genderStr == "f") + result.set_gender(ServerInfo_User::Female); + + const QString realName = query->value(5).toString(); if (!realName.isEmpty()) result.set_real_name(realName.toStdString()); - const QByteArray avatarBmp = query->value(8).toByteArray(); + const QByteArray avatarBmp = query->value(6).toByteArray(); if (avatarBmp.size()) result.set_avatar_bmp(avatarBmp.data(), avatarBmp.size()); - const QDateTime regDate = query->value(9).toDateTime(); - if (!regDate.toString(Qt::ISODate).isEmpty()) { - // the registration date is in utc - qint64 accountAgeInSeconds = regDate.secsTo(QDateTime::currentDateTimeUtc()); + const QDateTime regDate = query->value(7).toDateTime(); + if(!regDate.toString(Qt::ISODate).isEmpty()) { + qint64 accountAgeInSeconds = regDate.secsTo(QDateTime::currentDateTime()); result.set_accountage_secs(accountAgeInSeconds); } - const QString email = query->value(10).toString(); + const QString email = query->value(8).toString(); if (!email.isEmpty()) result.set_email(email.toStdString()); - const QString clientid = query->value(11).toString(); + const QString clientid = query->value(9).toString(); if (!clientid.isEmpty()) result.set_clientid(clientid.toStdString()); } @@ -658,10 +534,7 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b if (!checkSql()) return result; - QSqlQuery *query = prepareQuery("select id, name, admin, country, privlevel, leftPawnColorOverride, " - "rightPawnColorOverride, realname, avatar_bmp, registrationDate, " - "email, clientid from {prefix}_users where " - "name = :name and active = 1"); + QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email, clientid from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; @@ -677,9 +550,8 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b void Servatrice_DatabaseInterface::clearSessionTables() { lockSessionTables(); - QSqlQuery *query = - prepareQuery("update {prefix}_sessions set end_time=now() where end_time is null and id_server = :id_server"); - query->bindValue(":id_server", server->getServerID()); + QSqlQuery *query = prepareQuery("update {prefix}_sessions set end_time=now() where end_time is null and id_server = :id_server"); + query->bindValue(":id_server", server->getServerId()); execSqlQuery(query); unlockSessionTables(); } @@ -700,20 +572,14 @@ bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName) { // Call only after lockSessionTables(). - QSqlQuery *query = prepareQuery( - "select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null"); - query->bindValue(":id_server", server->getServerID()); + QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null"); + query->bindValue(":id_server", server->getServerId()); query->bindValue(":user_name", userName); - if (!execSqlQuery(query)) { - return false; - }; + execSqlQuery(query); return query->next(); } -qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, - const QString &address, - const QString &clientId, - const QString &connectionType) +qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address, const QString &clientId) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return -1; @@ -721,14 +587,11 @@ qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, if (!checkSql()) return -1; - QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, " - "clientid, connection_type) values(:user_name, :id_server, :ip_address, NOW(), " - ":client_id, :connection_type)"); + QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, clientid) values(:user_name, :id_server, :ip_address, NOW(), :client_id)"); query->bindValue(":user_name", userName); - query->bindValue(":id_server", server->getServerID()); + query->bindValue(":id_server", server->getServerId()); query->bindValue(":ip_address", address); query->bindValue(":client_id", clientId); - query->bindValue(":connection_type", connectionType); if (execSqlQuery(query)) return query->lastInsertId().toInt(); return -1; @@ -742,9 +605,15 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId) if (!checkSql()) return; - auto *query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session"); + QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write"); + execSqlQuery(query); + + query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session"); query->bindValue(":id_session", sessionId); execSqlQuery(query); + + query = prepareQuery("unlock tables"); + execSqlQuery(query); } QMap Servatrice_DatabaseInterface::getBuddyList(const QString &name) @@ -754,10 +623,7 @@ QMap Servatrice_DatabaseInterface::getBuddyList(const if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); - QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country, a.privlevel, " - "a.leftPawnColorOverride, a.rightPawnColorOverride from {prefix}_users a " - "left join {prefix}_buddylist b on a.id = b.id_user2 left join {prefix}_users " - "c on b.id_user1 = c.id where c.name = :name"); + QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country from {prefix}_users a left join {prefix}_buddylist b on a.id = b.id_user2 left join {prefix}_users c on b.id_user1 = c.id where c.name = :name"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; @@ -777,10 +643,7 @@ QMap Servatrice_DatabaseInterface::getIgnoreList(const if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); - QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country, a.privlevel, " - "a.leftPawnColorOverride, a.rightPawnColorOverride from {prefix}_users a " - "left join {prefix}_ignorelist b on a.id = b.id_user2 left join {prefix}_users " - "c on b.id_user1 = c.id where c.name = :name"); + QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country from {prefix}_users a left join {prefix}_ignorelist b on a.id = b.id_user2 left join {prefix}_users c on b.id_user1 = c.id where c.name = :name"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; @@ -802,10 +665,7 @@ int Servatrice_DatabaseInterface::getNextGameId() return -1; QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())"); - - if (!execSqlQuery(query)) { - return -1; - } + execSqlQuery(query); return query->lastInsertId().toInt(); } @@ -815,26 +675,18 @@ int Servatrice_DatabaseInterface::getNextReplayId() if (!checkSql()) return -1; - QSqlQuery *query = prepareQuery("insert into {prefix}_replays (id_game) values (NULL)"); - - if (!execSqlQuery(query)) { - return -1; - } + QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()"); + execSqlQuery(query); return query->lastInsertId().toInt(); } -void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, - const QStringList &roomGameTypes, - const ServerInfo_Game &gameInfo, - const QSet &allPlayersEver, - const QSet &allSpectatorsEver, - const QList &replayList) +void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { if (!checkSql()) return; - if (!settingsCache->value("game/store_replays", 1).toBool()) + if (!settingsCache->value("game/store_replays", 1).toBool() ) return; QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames; @@ -858,29 +710,18 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, QVariantList replayIds, replayGameIds, replayDurations, replayBlobs; for (int i = 0; i < replayList.size(); ++i) { QByteArray blob; -#if GOOGLE_PROTOBUF_VERSION > 3001000 - const unsigned int size = static_cast(replayList[i]->ByteSizeLong()); -#else - const unsigned int size = static_cast(replayList[i]->ByteSize()); -#endif + const unsigned int size = replayList[i]->ByteSize(); blob.resize(size); - qulonglong replayId = replayList[i]->replay_id(); - if (replayList[i]->SerializeToArray(blob.data(), size)) { + replayList[i]->SerializeToArray(blob.data(), size); - replayIds.append(QVariant(replayId)); - replayGameIds.append(gameInfo.game_id()); - replayDurations.append(replayList[i]->duration_seconds()); - replayBlobs.append(blob); - } else { - qCWarning(DatabaseInterfaceLog) - << "failed to serialise replay, id:" << replayId << "game:" << gameInfo.game_id(); - } + replayIds.append(QVariant((qulonglong) replayList[i]->replay_id())); + replayGameIds.append(gameInfo.game_id()); + replayDurations.append(replayList[i]->duration_seconds()); + replayBlobs.append(blob); } { - QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, " - "creator_name=:creator_name, password=:password, game_types=:game_types, " - "player_count=:player_count, time_finished=now() where id=:id_game"); + QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game"); query->bindValue(":room_name", roomName); query->bindValue(":id_game", gameInfo.game_id()); query->bindValue(":descr", QString::fromStdString(gameInfo.description())); @@ -892,15 +733,13 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, return; } { - QSqlQuery *query = - prepareQuery("insert into {prefix}_games_players (id_game, player_name) values (:id_game, :player_name)"); + QSqlQuery *query = prepareQuery("insert into {prefix}_games_players (id_game, player_name) values (:id_game, :player_name)"); query->bindValue(":id_game", gameIds1); query->bindValue(":player_name", playerNames); query->execBatch(); } { - QSqlQuery *query = prepareQuery( - "update {prefix}_replays set id_game=:id_game, duration=:duration, replay=:replay where id=:id_replay"); + QSqlQuery *query = prepareQuery("update {prefix}_replays set id_game=:id_game, duration=:duration, replay=:replay where id=:id_replay"); query->bindValue(":id_replay", replayIds); query->bindValue(":id_game", replayGameIds); query->bindValue(":duration", replayDurations); @@ -908,8 +747,7 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, query->execBatch(); } { - QSqlQuery *query = prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name) values " - "(:id_game, :id_player, :replay_name)"); + QSqlQuery *query = prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name) values (:id_game, :id_player, :replay_name)"); query->bindValue(":id_game", gameIds2); query->bindValue(":id_player", userIds); query->bindValue(":replay_name", replayNames); @@ -921,8 +759,7 @@ DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int user { checkSql(); - QSqlQuery *query = - prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user"); + QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user"); query->bindValue(":id", deckId); query->bindValue(":id_user", userId); execSqlQuery(query); @@ -935,33 +772,28 @@ DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int user return deck; } -void Servatrice_DatabaseInterface::logMessage(const int senderId, - const QString &senderName, - const QString &senderIp, - const QString &logMessage, - LogMessage_TargetType targetType, - const int targetId, - const QString &targetName) +void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName) { QString targetTypeString; - switch (targetType) { + switch(targetType) + { case MessageTargetRoom: - if (!settingsCache->value("logging/log_user_msg_room", 0).toBool()) + if(!settingsCache->value("logging/log_user_msg_room", 0).toBool()) return; targetTypeString = "room"; break; case MessageTargetGame: - if (!settingsCache->value("logging/log_user_msg_game", 0).toBool()) + if(!settingsCache->value("logging/log_user_msg_game", 0).toBool()) return; targetTypeString = "game"; break; case MessageTargetChat: - if (!settingsCache->value("logging/log_user_msg_chat", 0).toBool()) + if(!settingsCache->value("logging/log_user_msg_chat", 0).toBool()) return; targetTypeString = "chat"; break; case MessageTargetIslRoom: - if (!settingsCache->value("logging/log_user_msg_isl", 0).toBool()) + if(!settingsCache->value("logging/log_user_msg_isl", 0).toBool()) return; targetTypeString = "room"; break; @@ -969,9 +801,7 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, return; } - QSqlQuery *query = prepareQuery("insert into {prefix}_log (log_time, sender_id, sender_name, sender_ip, " - "log_message, target_type, target_id, target_name) values (now(), :sender_id, " - ":sender_name, :sender_ip, :log_message, :target_type, :target_id, :target_name)"); + QSqlQuery *query = prepareQuery("insert into {prefix}_log (log_time, sender_id, sender_name, sender_ip, log_message, target_type, target_id, target_name) values (now(), :sender_id, :sender_name, :sender_ip, :log_message, :target_type, :target_id, :target_name)"); query->bindValue(":sender_id", senderId < 1 ? QVariant() : senderId); query->bindValue(":sender_name", senderName); query->bindValue(":sender_ip", senderIp); @@ -982,85 +812,60 @@ void Servatrice_DatabaseInterface::logMessage(const int senderId, execSqlQuery(query); } -bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, - const QString &password, - bool passwordNeedsHash) +bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword) { - QString passwordSha512 = password; - if (passwordNeedsHash) { - passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); - } - - QSqlQuery *passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password, " - "passwordLastChangedDate = NOW() where name = :name"); - passwordQuery->bindValue(":password", passwordSha512); - passwordQuery->bindValue(":name", user); - if (execSqlQuery(passwordQuery)) + if(server->getAuthenticationMethod() != Servatrice::AuthenticationSql) return true; - return false; -} - -bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, - const QString &oldPassword, - bool oldPasswordNeedsHash, - const QString &newPassword, - bool newPasswordNeedsHash) -{ - if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql) - return false; - if (!checkSql()) - return false; + return true; QString error; if (!usernameIsValid(user, error)) - return false; + return true; QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); - if (!execSqlQuery(passwordQuery)) { - qCWarning(DatabaseInterfaceLog) << "Change password denied: SQL error"; - return false; + qDebug("Change password denied: SQL error"); + return true; } if (!passwordQuery->next()) - return false; + return true; - const QString correctPasswordSha512 = passwordQuery->value(0).toString(); - QString oldPasswordSha512 = oldPassword; - if (oldPasswordNeedsHash) { - QString salt = correctPasswordSha512.left(16); - oldPasswordSha512 = PasswordHasher::computeHash(oldPassword, salt); + const QString correctPassword = passwordQuery->value(0).toString(); + if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16))) + return true; + + QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt()); + + passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name"); + passwordQuery->bindValue(":password", passwordSha512); + passwordQuery->bindValue(":name", user); + if (!execSqlQuery(passwordQuery)) { + qDebug("Change password denied: SQL error"); + return true; } - if (correctPasswordSha512 != oldPasswordSha512) - return false; - - return changeUserPassword(user, newPassword, newPasswordNeedsHash); + return false; } -int Servatrice_DatabaseInterface::getActiveUserCount(QString connectionType) +int Servatrice_DatabaseInterface::getActiveUserCount() { int userCount = 0; if (!checkSql()) return userCount; - QString text = "select count(*) from {prefix}_sessions where id_server = :serverid AND end_time is NULL"; - if (!connectionType.isEmpty()) - text += " AND connection_type = :connection_type"; - QSqlQuery *query = prepareQuery(text); - - query->bindValue(":serverid", server->getServerID()); - if (!connectionType.isEmpty()) - query->bindValue(":connection_type", connectionType); - - if (!execSqlQuery(query)) + QSqlQuery *query = prepareQuery("select count(*) from {prefix}_sessions where id_server = :serverid AND end_time is NULL"); + query->bindValue(":serverid", server->getServerId()); + if (!execSqlQuery(query)){ return userCount; + } - if (query->next()) + if (query->next()){ userCount = query->value(0).toInt(); + } return userCount; } @@ -1075,20 +880,20 @@ void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, query->bindValue(":clientid", userClientID); query->bindValue(":username", userName); execSqlQuery(query); + } -void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userName, const QString &clientVersion) -{ +void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userName, const QString &clientVersion) { if (!checkSql()) return; - int usersID = 0; + int usersID; QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :user_name"); query->bindValue(":user_name", userName); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to locate user id when updating users last login data: SQL Error"; + qDebug("Failed to locate user id when updating users last login data: SQL Error"); return; } @@ -1097,7 +902,7 @@ void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userN } if (usersID) { - int userCount = 0; + int userCount; query = prepareQuery("select count(id) from {prefix}_user_analytics where id = :user_id"); query->bindValue(":user_id", usersID); if (!execSqlQuery(query)) @@ -1108,14 +913,12 @@ void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userN } if (!userCount) { - query = prepareQuery( - "insert into {prefix}_user_analytics (id,client_ver,last_login) values (:user_id,:client_ver,NOW())"); + query = prepareQuery("insert into {prefix}_user_analytics (id,client_ver,last_login) values (:user_id,:client_ver,NOW())"); query->bindValue(":user_id", usersID); query->bindValue(":client_ver", clientVersion); execSqlQuery(query); } else { - query = prepareQuery( - "update {prefix}_user_analytics set last_login = NOW(), client_ver = :client_ver where id = :user_id"); + query = prepareQuery("update {prefix}_user_analytics set last_login = NOW(), client_ver = :client_ver where id = :user_id"); query->bindValue(":client_ver", clientVersion); query->bindValue(":user_id", usersID); execSqlQuery(query); @@ -1131,17 +934,15 @@ QList Servatrice_DatabaseInterface::getUserBanHistory(const QStr if (!checkSql()) return results; - QSqlQuery *query = - prepareQuery("SELECT A.id_admin, A.time_from, A.minutes, A.reason, A.visible_reason, B.name AS name_admin FROM " - "{prefix}_bans A LEFT JOIN {prefix}_users B ON A.id_admin=B.id WHERE A.user_name = :user_name"); + QSqlQuery *query = prepareQuery("SELECT A.id_admin, A.time_from, A.minutes, A.reason, A.visible_reason, B.name AS name_admin FROM {prefix}_bans A LEFT JOIN {prefix}_users B ON A.id_admin=B.id WHERE A.user_name = :user_name"); query->bindValue(":user_name", userName); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect ban history information: SQL Error"; + qDebug("Failed to collect ban history information: SQL Error"); return results; } - while (query->next()) { + while (query->next()){ banDetails.set_admin_id(QString(query->value(0).toString()).toStdString()); banDetails.set_admin_name(QString(query->value(5).toString()).toStdString()); banDetails.set_ban_time(QString(query->value(1).toString()).toStdString()); @@ -1154,25 +955,20 @@ QList Servatrice_DatabaseInterface::getUserBanHistory(const QStr return results; } -bool Servatrice_DatabaseInterface::addWarning(const QString userName, - const QString adminName, - const QString warningReason, - const QString clientID) +bool Servatrice_DatabaseInterface::addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID) { if (!checkSql()) return false; int userID = getUserIdInDB(userName); - QSqlQuery *query = - prepareQuery("insert into {prefix}_warnings (user_id,user_name,mod_name,reason,time_of,clientid) values " - "(:user_id,:user_name,:mod_name,:warn_reason,NOW(),:client_id)"); + QSqlQuery *query = prepareQuery("insert into {prefix}_warnings (user_id,user_name,mod_name,reason,time_of,clientid) values (:user_id,:user_name,:mod_name,:warn_reason,NOW(),:client_id)"); query->bindValue(":user_id", userID); query->bindValue(":user_name", userName); query->bindValue(":mod_name", adminName); query->bindValue(":warn_reason", warningReason); query->bindValue(":client_id", clientID); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect create warning history information: SQL Error"; + qDebug("Failed to collect create warning history information: SQL Error"); return false; } @@ -1188,16 +984,15 @@ QList Servatrice_DatabaseInterface::getUserWarnHistory(const return results; int userID = getUserIdInDB(userName); - QSqlQuery *query = - prepareQuery("SELECT user_name, mod_name, reason, time_of FROM {prefix}_warnings WHERE user_id = :user_id"); + QSqlQuery *query = prepareQuery("SELECT user_name, mod_name, reason, time_of FROM {prefix}_warnings WHERE user_id = :user_id"); query->bindValue(":user_id", userID); if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect warning history information: SQL Error"; + qDebug("Failed to collect warning history information: SQL Error"); return results; } - while (query->next()) { + while (query->next()){ warnDetails.set_user_name(QString(query->value(0).toString()).toStdString()); warnDetails.set_admin_name(QString(query->value(1).toString()).toStdString()); warnDetails.set_reason(QString(query->value(2).toString()).toStdString()); @@ -1208,16 +1003,7 @@ QList Servatrice_DatabaseInterface::getUserWarnHistory(const return results; } -QList Servatrice_DatabaseInterface::getMessageLogHistory(const QString &user, - const QString &ipaddress, - const QString &gamename, - const QString &gameid, - const QString &message, - bool &chat, - bool &game, - bool &room, - int &range, - int &maxresults) +QList Servatrice_DatabaseInterface::getMessageLogHistory(const QString &user, const QString &ipaddress, const QString &gamename, const QString &gameid, const QString &message, bool &chat, bool &game, bool &room, int &range, int &maxresults) { QList results; @@ -1226,13 +1012,8 @@ QList Servatrice_DatabaseInterface::getMessageLogHistory if (!checkSql()) return results; - if (user.isEmpty() && ipaddress.isEmpty() && gameid.isEmpty() && gamename.isEmpty()) { - // To ensure quick results and minimal lag, require an indexed field - return results; - } - // BUILD QUERY STRING BASED ON PASSED IN VALUES - QString queryString = "SELECT * FROM {prefix}_log WHERE `sender_ip` IS NOT NULL"; + QString queryString = "SELECT * FROM cockatrice_log WHERE `sender_ip` IS NOT NULL"; if (!user.isEmpty()) queryString.append(" AND (`sender_name` = :user_name OR `target_name` = :user_name)"); @@ -1277,30 +1058,16 @@ QList Servatrice_DatabaseInterface::getMessageLogHistory queryString.append(" LIMIT :limit_size"); QSqlQuery *query = prepareQuery(queryString); - if (!user.isEmpty()) { - query->bindValue(":user_name", user); - } - if (!ipaddress.isEmpty()) { - query->bindValue(":ip_to_find", ipaddress); - } - if (!gameid.isEmpty()) { - query->bindValue(":game_id", gameid); - } - if (!gamename.isEmpty()) { - query->bindValue(":game_name", gamename); - } - if (!message.isEmpty()) { - query->bindValue(":log_message", message); - } - if (range) { - query->bindValue(":range_time", range); - } - if (maxresults) { - query->bindValue(":limit_size", maxresults); - } + if (!user.isEmpty()) { query->bindValue(":user_name", user); } + if (!ipaddress.isEmpty()) { query->bindValue(":ip_to_find", ipaddress); } + if (!gameid.isEmpty()) { query->bindValue(":game_id", gameid); } + if (!gamename.isEmpty()) { query->bindValue(":game_name", gamename); } + if (!message.isEmpty()) { query->bindValue(":log_message", message); } + if (range) { query->bindValue(":range_time", range); } + if (maxresults) { query->bindValue(":limit_size", maxresults); } if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) << "Failed to collect log history information: SQL Error"; + qDebug("Failed to collect log history information: SQL Error"); return results; } @@ -1318,145 +1085,3 @@ QList Servatrice_DatabaseInterface::getMessageLogHistory return results; } - -int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email) -{ - if (!checkSql()) - return 0; - - QSqlQuery *query = prepareQuery("SELECT count(email) FROM {prefix}_users WHERE email = :user_email"); - query->bindValue(":user_email", email); - - if (!execSqlQuery(query)) { - qCWarning(DatabaseInterfaceLog) - << "Failed to identify the number of users accounts for users email address: SQL Error"; - return 0; - } - - if (query->next()) - return query->value(0).toInt(); - - return 0; -} - -bool Servatrice_DatabaseInterface::addForgotPassword(const QString &user) -{ - if (!checkSql()) - return false; - - if (!updateUserToken(PasswordHasher::generateActivationToken(), user)) - return false; - - QSqlQuery *query = prepareQuery("insert into {prefix}_forgot_password (name,requestDate) values (:username,NOW())"); - query->bindValue(":username", user); - if (execSqlQuery(query)) - return true; - - return false; -} - -bool Servatrice_DatabaseInterface::removeForgotPassword(const QString &user) -{ - if (!checkSql()) - return false; - - QSqlQuery *query = prepareQuery("delete from {prefix}_forgot_password where name = :username"); - query->bindValue(":username", user); - if (execSqlQuery(query)) - return true; - - return false; -} - -bool Servatrice_DatabaseInterface::doesForgotPasswordExist(const QString &user) -{ - if (!checkSql()) - return false; - - QSqlQuery *query = prepareQuery("select count(name) from {prefix}_forgot_password where name = :user_name AND " - "requestDate > (now() - interval :minutes minute)"); - query->bindValue(":user_name", user); - query->bindValue(":minutes", QString::number(server->getForgotPasswordTokenLife())); - - if (!execSqlQuery(query)) - return false; - - if (query->next()) - if (query->value("count(name)").toInt() > 0) - return true; - - return false; -} - -bool Servatrice_DatabaseInterface::updateUserToken(const QString &token, const QString &user) -{ - if (!checkSql()) - return false; - - if (token.isEmpty() || user.isEmpty()) - return false; - - QSqlQuery *query = prepareQuery("update {prefix}_users set token = :token where name = :user_name"); - query->bindValue(":user_name", user); - query->bindValue(":token", token); - - if (execSqlQuery(query)) - return true; - - return false; -} - -bool Servatrice_DatabaseInterface::validateTableColumnStringData(const QString &table, - const QString &column, - const QString &_user, - const QString &_datatocheck) -{ - if (!checkSql()) - return false; - - if (table.isEmpty() || column.isEmpty() || _user.isEmpty() || _datatocheck.isEmpty()) - return false; - - QString formatedQuery = QString("select %1 from %2 where name = :user_name").arg(column).arg(table); - QSqlQuery *query = prepareQuery(formatedQuery); - query->bindValue(":user_name", _user); - - if (!execSqlQuery(query)) - return false; - - if (query->next()) - if (query->value(column).toString().toLower() == _datatocheck.toLower()) - return true; - - return false; -} - -void Servatrice_DatabaseInterface::addAuditRecord(const QString &user, - const QString &ipaddress, - const QString &clientid, - const QString &action, - const QString &details, - const bool &results = false) -{ - if (!checkSql()) - return; - - if (!server->getEnableAudit()) - return; - - if (user.isEmpty() || ipaddress.isEmpty() || clientid.isEmpty() || action.isEmpty()) - return; - - QSqlQuery *query = prepareQuery("insert into {prefix}_audit " - "(id_server,name,ip_address,clientid,incidentDate,action,results,details) values " - "(:idserver,:username,:ipaddress,:clientid,NOW(),:action,:results,:details)"); - query->bindValue(":idserver", server->getServerID()); - query->bindValue(":username", user); - query->bindValue(":ipaddress", ipaddress); - query->bindValue(":clientid", clientid); - query->bindValue(":action", action); - query->bindValue(":results", results ? "success" : "fail"); - - query->bindValue(":details", details); - execSqlQuery(query); -} diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 68080404c..149121316 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -1,21 +1,19 @@ #ifndef SERVATRICE_DATABASE_INTERFACE_H #define SERVATRICE_DATABASE_INTERFACE_H -#include -#include #include #include -#include -#include -#include -#include +#include +#include -#define DATABASE_SCHEMA_VERSION 34 +#include "server.h" +#include "server_database_interface.h" + +#define DATABASE_SCHEMA_VERSION 12 class Servatrice; -class Servatrice_DatabaseInterface : public Server_DatabaseInterface -{ +class Servatrice_DatabaseInterface : public Server_DatabaseInterface { Q_OBJECT private: int instanceId; @@ -31,122 +29,59 @@ private: bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining); protected: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, - const QString &user, - const QString &password, - const QString &clientId, - QString &reasonStr, - int &banSecondsLeft, - bool passwordNeedsHash) override; + AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft); public slots: void initDatabase(const QSqlDatabase &_sqlDatabase); public: - explicit Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); - ~Servatrice_DatabaseInterface() override; - bool initDatabase(const QString &type, - const QString &hostName, - const QString &databaseName, - const QString &userName, - const QString &password); + Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server); + ~Servatrice_DatabaseInterface(); + bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName, + const QString &userName, const QString &password); bool openDatabase(); bool checkSql(); - QSqlQuery *prepareQuery(const QString &queryText); + QSqlQuery * prepareQuery(const QString &queryText); bool execSqlQuery(QSqlQuery *query); - const QSqlDatabase &getDatabase() - { - return sqlDatabase; - } + const QSqlDatabase &getDatabase() { return sqlDatabase; } - bool activeUserExists(const QString &user) override; - bool userExists(const QString &user) override; - QString getUserSalt(const QString &user) override; + bool activeUserExists(const QString &user); + bool userExists(const QString &user); int getUserIdInDB(const QString &name); - QMap getBuddyList(const QString &name) override; - QMap getIgnoreList(const QString &name) override; - bool isInBuddyList(const QString &whoseList, const QString &who) override; - bool isInIgnoreList(const QString &whoseList, const QString &who) override; - ServerInfo_User getUserData(const QString &name, bool withId = false) override; - void storeGameInformation(const QString &roomName, - const QStringList &roomGameTypes, - const ServerInfo_Game &gameInfo, - const QSet &allPlayersEver, - const QSet &allSpectatorsEver, - const QList &replayList) override; - DeckList *getDeckFromDatabase(int deckId, int userId) override; + QMap getBuddyList(const QString &name); + QMap getIgnoreList(const QString &name); + bool isInBuddyList(const QString &whoseList, const QString &who); + bool isInIgnoreList(const QString &whoseList, const QString &who); + ServerInfo_User getUserData(const QString &name, bool withId = false); + void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, + const QSet &allPlayersEver, const QSet&allSpectatorsEver, const QList &replayList); + DeckList *getDeckFromDatabase(int deckId, int userId); - int getNextGameId() override; - int getNextReplayId() override; - int getActiveUserCount(QString connectionType = QString()) override; + int getNextGameId(); + int getNextReplayId(); + int getActiveUserCount(); + qint64 startSession(const QString &userName, const QString &address, const QString &clientId); + void endSession(qint64 sessionId); + void clearSessionTables(); + void lockSessionTables(); + void unlockSessionTables(); + bool userSessionExists(const QString &userName); + bool usernameIsValid(const QString &user, QString & error); + bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining); - qint64 startSession(const QString &userName, - const QString &address, - const QString &clientId, - const QString &connectionType) override; - void endSession(qint64 sessionId) override; - void clearSessionTables() override; - void lockSessionTables() override; - void unlockSessionTables() override; - bool userSessionExists(const QString &userName) override; - bool usernameIsValid(const QString &user, QString &error) override; - bool checkUserIsBanned(const QString &ipAddress, - const QString &userName, - const QString &clientId, - QString &banReason, - int &banSecondsRemaining) override; - int checkNumberOfUserAccounts(const QString &email) override; - bool registerUser(const QString &userName, - const QString &realName, - const QString &password, - bool passwordNeedsHash, - const QString &emailAddress, - const QString &country, - bool active = false) override; - bool activateUser(const QString &userName, const QString &token) override; - void updateUsersClientID(const QString &userName, const QString &userClientID) override; - void updateUsersLastLoginData(const QString &userName, const QString &clientVersion) override; - void logMessage(const int senderId, - const QString &senderName, - const QString &senderIp, - const QString &logMessage, - LogMessage_TargetType targetType, - const int targetId, - const QString &targetName) override; - bool changeUserPassword(const QString &user, const QString &password, bool passwordNeedsHash) override; - bool changeUserPassword(const QString &user, - const QString &oldPassword, - bool oldPasswordNeedsHash, - const QString &newPassword, - bool newPasswordNeedsHash) override; + bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, + const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false); + bool activateUser(const QString &userName, const QString &token); + void updateUsersClientID(const QString &userName, const QString &userClientID); + void updateUsersLastLoginData(const QString &userName, const QString &clientVersion); + void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, + LogMessage_TargetType targetType, const int targetId, const QString &targetName); + bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword); + QChar getGenderChar(ServerInfo_User_Gender const &gender); QList getUserBanHistory(const QString userName); - bool - addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); + bool addWarning(const QString userName, const QString adminName, const QString warningReason, const QString clientID); QList getUserWarnHistory(const QString userName); - QList getMessageLogHistory(const QString &user, - const QString &ipaddress, - const QString &gamename, - const QString &gameid, - const QString &message, - bool &chat, - bool &game, - bool &room, - int &range, - int &maxresults); - bool addForgotPassword(const QString &user); - bool removeForgotPassword(const QString &user) override; - bool doesForgotPasswordExist(const QString &user); - bool updateUserToken(const QString &token, const QString &user); - bool validateTableColumnStringData(const QString &table, - const QString &column, - const QString &_user, - const QString &_datatocheck); - void addAuditRecord(const QString &user, - const QString &ipaddress, - const QString &clientid, - const QString &action, - const QString &details, - const bool &results); + QList getMessageLogHistory(const QString &user, const QString &ipaddress, const QString &gamename, const QString &gameid, const QString &message, bool &chat, bool &game, bool &room, int &range, int &maxresults); }; #endif diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index de0befacb..2b841acef 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -1,12 +1,10 @@ #include "server_logger.h" - #include "settingscache.h" - -#include -#include #include #include +#include #include +#include #include ServerLogger::ServerLogger(bool _logToConsole, QObject *parent) @@ -32,8 +30,9 @@ void ServerLogger::startLog(const QString &logFileName) return; } + logFile = new QFile(logFileName, this); - if (!logFile->open(QIODevice::Append)) { + if(!logFile->open(QIODevice::Append)) { std::cerr << "ERROR: can't open() logfile." << std::endl; delete logFile; logFile = 0; @@ -41,32 +40,32 @@ void ServerLogger::startLog(const QString &logFileName) } } else logFile = 0; - + connect(this, SIGNAL(sigFlushBuffer()), this, SLOT(flushBuffer()), Qt::QueuedConnection); } -void ServerLogger::logMessage(const QString &message, void *caller) +void ServerLogger::logMessage(QString message, void *caller) { if (!logFile) return; - + QString callerString; if (caller) - callerString = QString::number((qulonglong)caller, 16) + " "; - - // filter out all log entries based on values in configuration file - bool shouldWeWriteLog = settingsCache->value("server/writelog", 1).toBool(); + callerString = QString::number((qulonglong) caller, 16) + " "; + + //filter out all log entries based on values in configuration file + bool shouldWeWriteLog = settingsCache->value("server/writelog",1).toBool(); QString logFilters = settingsCache->value("server/logfilters").toString(); - QStringList listlogFilters = logFilters.split(",", Qt::SkipEmptyParts); - bool shouldWeSkipLine = false; - + QStringList listlogFilters = logFilters.split(",", QString::SkipEmptyParts); + bool shouldWeSkipLine = false; + if (!shouldWeWriteLog) return; - if (!logFilters.trimmed().isEmpty()) { + if (!logFilters.trimmed().isEmpty()){ shouldWeSkipLine = true; - for (const QString &logFilter : listlogFilters) { - if (message.contains(logFilter, Qt::CaseInsensitive)) { + foreach(QString logFilter, listlogFilters){ + if (message.contains(logFilter, Qt::CaseInsensitive)){ shouldWeSkipLine = false; break; } @@ -86,11 +85,10 @@ void ServerLogger::flushBuffer() { if (flushRunning) return; - + flushRunning = true; QTextStream stream(logFile); - forever - { + forever { bufferMutex.lock(); if (buffer.isEmpty()) { bufferMutex.unlock(); @@ -99,10 +97,10 @@ void ServerLogger::flushBuffer() } QString message = buffer.takeFirst(); bufferMutex.unlock(); - + stream << message << "\n"; stream.flush(); - + if (logToConsole) std::cout << message.toStdString() << std::endl; } @@ -114,11 +112,9 @@ void ServerLogger::rotateLogs() return; flushBuffer(); - + logFile->close(); - if (!logFile->open(QIODevice::Append)) { - std::cerr << "ERROR: Failed to open log file for writing!" << std::endl; - } + logFile->open(QIODevice::Append); } QFile *ServerLogger::logFile; diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index 0ad092b66..478d0460b 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -1,36 +1,34 @@ #ifndef SERVER_LOGGER_H #define SERVER_LOGGER_H -#include #include -#include #include +#include #include +#include class QFile; class Server_ProtocolHandler; -class ServerLogger : public QObject -{ - Q_OBJECT +class ServerLogger : public QObject { + Q_OBJECT public: - ServerLogger(bool _logToConsole, QObject *parent = 0); - ~ServerLogger(); + ServerLogger(bool _logToConsole, QObject *parent = 0); + ~ServerLogger(); public slots: - void startLog(const QString &logFileName); - void logMessage(const QString &message, void *caller = 0); - void rotateLogs(); + void startLog(const QString &logFileName); + void logMessage(QString message, void *caller = 0); + void rotateLogs(); private slots: - void flushBuffer(); + void flushBuffer(); signals: - void sigFlushBuffer(); - + void sigFlushBuffer(); private: - bool logToConsole; - static QFile *logFile; - bool flushRunning; - QStringList buffer; - QMutex bufferMutex; + bool logToConsole; + static QFile *logFile; + bool flushRunning; + QStringList buffer; + QMutex bufferMutex; }; #endif diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 41e61ddec..e55104ca6 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -18,134 +18,223 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "serversocketinterface.h" - -#include "email_parser.h" -#include "main.h" -#include "servatrice.h" -#include "servatrice_database_interface.h" -#include "server_logger.h" -#include "settingscache.h" -#include "version_string.h" - -#include -#include -#include -#include -#include +#include #include #include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "settingscache.h" +#include "serversocketinterface.h" +#include "servatrice.h" +#include "servatrice_database_interface.h" +#include "decklist.h" +#include "server_player.h" +#include "main.h" +#include "server_logger.h" +#include "server_response_containers.h" +#include "pb/commands.pb.h" +#include "pb/command_deck_list.pb.h" +#include "pb/command_deck_upload.pb.h" +#include "pb/command_deck_download.pb.h" +#include "pb/command_deck_new_dir.pb.h" +#include "pb/command_deck_del_dir.pb.h" +#include "pb/command_deck_del.pb.h" +#include "pb/command_replay_list.pb.h" +#include "pb/command_replay_download.pb.h" +#include "pb/command_replay_modify_match.pb.h" +#include "pb/command_replay_delete_match.pb.h" +#include "pb/event_connection_closed.pb.h" +#include "pb/event_server_message.pb.h" +#include "pb/event_server_identification.pb.h" +#include "pb/event_add_to_list.pb.h" +#include "pb/event_remove_from_list.pb.h" +#include "pb/event_notify_user.pb.h" +#include "pb/event_user_message.pb.h" +#include "pb/response_ban_history.pb.h" +#include "pb/response_deck_list.pb.h" +#include "pb/response_deck_download.pb.h" +#include "pb/response_deck_upload.pb.h" +#include "pb/response_register.pb.h" +#include "pb/response_replay_list.pb.h" +#include "pb/response_replay_download.pb.h" +#include "pb/response_warn_history.pb.h" +#include "pb/response_warn_list.pb.h" +#include "pb/response_viewlog_history.pb.h" +#include "pb/serverinfo_replay.pb.h" +#include "pb/serverinfo_user.pb.h" +#include "pb/serverinfo_deckstorage.pb.h" +#include "pb/serverinfo_ban.pb.h" +#include "pb/serverinfo_chat_message.pb.h" -inline Q_LOGGING_CATEGORY(AbstractServerSocketInterfaceLog, "abstract_server_socket_interface"); -inline Q_LOGGING_CATEGORY(TcpServerSocketInterfaceLog, "tcp_server_socket_interface"); -inline Q_LOGGING_CATEGORY(WebsocketServerSocketInterfaceLog, "websocket_server_socket_interface"); +#include "version_string.h" +#include +#include static const int protocolVersion = 14; -AbstractServerSocketInterface::AbstractServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent) - : Server_ProtocolHandler(_server, _databaseInterface, parent), servatrice(_server), - sqlInterface(reinterpret_cast(databaseInterface)) +ServerSocketInterface::ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent) + : Server_ProtocolHandler(_server, _databaseInterface, parent), + servatrice(_server), + sqlInterface(reinterpret_cast(databaseInterface)), + messageInProgress(false), + handshakeStarted(false) { + socket = new QTcpSocket(this); + socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); + // Never call flushOutputQueue directly from outputQueueChanged. In case of a socket error, - // it could lead to this object being destroyed while another function is still on the call stack. -> mutex - // deadlocks etc. + // it could lead to this object being destroyed while another function is still on the call stack. -> mutex deadlocks etc. connect(this, SIGNAL(outputQueueChanged()), this, SLOT(flushOutputQueue()), Qt::QueuedConnection); } -bool AbstractServerSocketInterface::initSession() +ServerSocketInterface::~ServerSocketInterface() +{ + logger->logMessage("ServerSocketInterface destructor", this); + + flushOutputQueue(); +} + +void ServerSocketInterface::initConnection(int socketDescriptor) +{ + // Add this object to the server's list of connections before it can receive socket events. + // Otherwise, in case a of a socket error, it could be removed from the list before it is added. + server->addClient(this); + + socket->setSocketDescriptor(socketDescriptor); + logger->logMessage(QString("Incoming connection: %1").arg(socket->peerAddress().toString()), this); + initSessionDeprecated(); +} + +void ServerSocketInterface::initSessionDeprecated() +{ + // dirty hack to make v13 client display the correct error message + + QByteArray buf; + buf.append(""); + socket->write(buf); + socket->flush(); +} + +bool ServerSocketInterface::initSession() { Event_ServerIdentification identEvent; identEvent.set_server_name(servatrice->getServerName().toStdString()); identEvent.set_server_version(VERSION_STRING); identEvent.set_protocol_version(protocolVersion); - if (servatrice->getAuthenticationMethod() == Servatrice::AuthenticationSql) { - identEvent.set_server_options(Event_ServerIdentification::SupportsPasswordHash); - } SessionEvent *identSe = prepareSessionEvent(identEvent); sendProtocolItem(*identSe); delete identSe; - // allow unlimited number of connections from the trusted sources - QString trustedSources = settingsCache->value("security/trusted_sources", "127.0.0.1,::1").toString(); - if (trustedSources.contains(getAddress(), Qt::CaseInsensitive)) - return true; + //limit the number of total users based on configuration settings + bool enforceUserLimit = settingsCache->value("security/enable_max_user_limit", false).toBool(); + if (enforceUserLimit){ + int userLimit = settingsCache->value("security/max_users_total", 500).toInt(); + int playerCount = (databaseInterface->getActiveUserCount() + 1); + if (playerCount > userLimit){ + std::cerr << "Max Users Total Limit Reached, please increase the max_users_total setting." << std::endl; + logger->logMessage(QString("Max Users Total Limit Reached, please increase the max_users_total setting."), this); + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::USER_LIMIT_REACHED); + SessionEvent *se = prepareSessionEvent(event); + sendProtocolItem(*se); + delete se; + return false; + } + } + //allow unlimited number of connections from the trusted sources + QString trustedSources = settingsCache->value("security/trusted_sources","127.0.0.1,::1").toString(); + if (trustedSources.contains(socket->peerAddress().toString(),Qt::CaseInsensitive)) + return true; + int maxUsers = servatrice->getMaxUsersPerAddress(); - if ((maxUsers > 0) && (servatrice->getUsersWithAddress(getPeerAddress()) > maxUsers)) { + if ((maxUsers > 0) && (servatrice->getUsersWithAddress(socket->peerAddress()) >= maxUsers)) { Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::TOO_MANY_CONNECTIONS); SessionEvent *se = prepareSessionEvent(event); sendProtocolItem(*se); delete se; + return false; } return true; } -void AbstractServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socketError) +void ServerSocketInterface::readClient() { - qCWarning(AbstractServerSocketInterfaceLog) << "Socket error:" << socketError; + QByteArray data = socket->readAll(); + servatrice->incRxBytes(data.size()); + inputBuffer.append(data); + + do { + if (!messageInProgress) { + if (inputBuffer.size() >= 4) { + messageLength = (((quint32) (unsigned char) inputBuffer[0]) << 24) + + (((quint32) (unsigned char) inputBuffer[1]) << 16) + + (((quint32) (unsigned char) inputBuffer[2]) << 8) + + ((quint32) (unsigned char) inputBuffer[3]); + inputBuffer.remove(0, 4); + messageInProgress = true; + } else + return; + } + if (inputBuffer.size() < messageLength) + return; + + CommandContainer newCommandContainer; + try { + newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength); + } + catch(std::exception &e) { + qDebug() << "Caught std::exception in" << __FILE__ << __LINE__ << +#ifdef _MSC_VER // Visual Studio + __FUNCTION__; +#else + __PRETTY_FUNCTION__; +#endif + qDebug() << "Exception:" << e.what(); + qDebug() << "Message coming from:" << getAddress(); + qDebug() << "Message length:" << messageLength; + qDebug() << "Message content:" << inputBuffer.toHex(); + } + catch(...) { + qDebug() << "Unhandled exception in" << __FILE__ << __LINE__ << +#ifdef _MSC_VER // Visual Studio + __FUNCTION__; +#else + __PRETTY_FUNCTION__; +#endif + qDebug() << "Message coming from:" << getAddress(); + } + + inputBuffer.remove(0, messageLength); + messageInProgress = false; + + // dirty hack to make v13 client display the correct error message + if (handshakeStarted) + processCommandContainer(newCommandContainer); + else if (!newCommandContainer.has_cmd_id()) { + handshakeStarted = true; + if (!initSession()) + prepareDestroy(); + } + // end of hack + } while (!inputBuffer.isEmpty()); +} + +void ServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socketError) +{ + qDebug() << "Socket error:" << socketError; prepareDestroy(); } -void AbstractServerSocketInterface::catchSocketDisconnected() -{ - prepareDestroy(); -} - -void AbstractServerSocketInterface::transmitProtocolItem(const ServerMessage &item) +void ServerSocketInterface::transmitProtocolItem(const ServerMessage &item) { outputQueueMutex.lock(); outputQueue.append(item); @@ -154,127 +243,98 @@ void AbstractServerSocketInterface::transmitProtocolItem(const ServerMessage &it emit outputQueueChanged(); } -void AbstractServerSocketInterface::logDebugMessage(const QString &message) +void ServerSocketInterface::flushOutputQueue() +{ + QMutexLocker locker(&outputQueueMutex); + if (outputQueue.isEmpty()) + return; + + int totalBytes = 0; + while (!outputQueue.isEmpty()) { + ServerMessage item = outputQueue.takeFirst(); + locker.unlock(); + + QByteArray buf; + unsigned int size = item.ByteSize(); + buf.resize(size + 4); + item.SerializeToArray(buf.data() + 4, size); + buf.data()[3] = (unsigned char) size; + buf.data()[2] = (unsigned char) (size >> 8); + buf.data()[1] = (unsigned char) (size >> 16); + buf.data()[0] = (unsigned char) (size >> 24); + // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. + socket->write(buf); + + totalBytes += size + 4; + locker.relock(); + } + locker.unlock(); + servatrice->incTxBytes(totalBytes); + // see above wrt mutex + socket->flush(); +} + +void ServerSocketInterface::logDebugMessage(const QString &message) { logger->logMessage(message, this); } -Response::ResponseCode AbstractServerSocketInterface::processExtendedSessionCommand(int cmdType, - const SessionCommand &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc) { - switch ((SessionCommand::SessionCommandType)cmdType) { - case SessionCommand::ADD_TO_LIST: - return cmdAddToList(cmd.GetExtension(Command_AddToList::ext), rc); - case SessionCommand::REMOVE_FROM_LIST: - return cmdRemoveFromList(cmd.GetExtension(Command_RemoveFromList::ext), rc); - case SessionCommand::DECK_LIST: - return cmdDeckList(cmd.GetExtension(Command_DeckList::ext), rc); - case SessionCommand::DECK_NEW_DIR: - return cmdDeckNewDir(cmd.GetExtension(Command_DeckNewDir::ext), rc); - case SessionCommand::DECK_DEL_DIR: - return cmdDeckDelDir(cmd.GetExtension(Command_DeckDelDir::ext), rc); - case SessionCommand::DECK_DEL: - return cmdDeckDel(cmd.GetExtension(Command_DeckDel::ext), rc); - case SessionCommand::DECK_UPLOAD: - return cmdDeckUpload(cmd.GetExtension(Command_DeckUpload::ext), rc); - case SessionCommand::DECK_DOWNLOAD: - return cmdDeckDownload(cmd.GetExtension(Command_DeckDownload::ext), rc); - case SessionCommand::REPLAY_LIST: - return cmdReplayList(cmd.GetExtension(Command_ReplayList::ext), rc); - case SessionCommand::REPLAY_DOWNLOAD: - return cmdReplayDownload(cmd.GetExtension(Command_ReplayDownload::ext), rc); - case SessionCommand::REPLAY_MODIFY_MATCH: - return cmdReplayModifyMatch(cmd.GetExtension(Command_ReplayModifyMatch::ext), rc); - case SessionCommand::REPLAY_DELETE_MATCH: - return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc); - case SessionCommand::REPLAY_GET_CODE: - return cmdReplayGetCode(cmd.GetExtension(Command_ReplayGetCode::ext), rc); - case SessionCommand::REPLAY_SUBMIT_CODE: - return cmdReplaySubmitCode(cmd.GetExtension(Command_ReplaySubmitCode::ext), rc); - case SessionCommand::REGISTER: - return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); - break; - case SessionCommand::ACTIVATE: - return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); - break; - case SessionCommand::FORGOT_PASSWORD_REQUEST: - return cmdForgotPasswordRequest(cmd.GetExtension(Command_ForgotPasswordRequest::ext), rc); - break; - case SessionCommand::FORGOT_PASSWORD_RESET: - return cmdForgotPasswordReset(cmd.GetExtension(Command_ForgotPasswordReset::ext), rc); - break; - case SessionCommand::FORGOT_PASSWORD_CHALLENGE: - return cmdForgotPasswordChallenge(cmd.GetExtension(Command_ForgotPasswordChallenge::ext), rc); - break; - case SessionCommand::ACCOUNT_EDIT: - return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc); - case SessionCommand::ACCOUNT_IMAGE: - return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc); - case SessionCommand::ACCOUNT_PASSWORD: - return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc); - case SessionCommand::REQUEST_PASSWORD_SALT: - return cmdRequestPasswordSalt(cmd.GetExtension(Command_RequestPasswordSalt::ext), rc); - break; - default: - return Response::RespFunctionNotAllowed; + switch ((SessionCommand::SessionCommandType) cmdType) { + case SessionCommand::ADD_TO_LIST: return cmdAddToList(cmd.GetExtension(Command_AddToList::ext), rc); + case SessionCommand::REMOVE_FROM_LIST: return cmdRemoveFromList(cmd.GetExtension(Command_RemoveFromList::ext), rc); + case SessionCommand::DECK_LIST: return cmdDeckList(cmd.GetExtension(Command_DeckList::ext), rc); + case SessionCommand::DECK_NEW_DIR: return cmdDeckNewDir(cmd.GetExtension(Command_DeckNewDir::ext), rc); + case SessionCommand::DECK_DEL_DIR: return cmdDeckDelDir(cmd.GetExtension(Command_DeckDelDir::ext), rc); + case SessionCommand::DECK_DEL: return cmdDeckDel(cmd.GetExtension(Command_DeckDel::ext), rc); + case SessionCommand::DECK_UPLOAD: return cmdDeckUpload(cmd.GetExtension(Command_DeckUpload::ext), rc); + case SessionCommand::DECK_DOWNLOAD: return cmdDeckDownload(cmd.GetExtension(Command_DeckDownload::ext), rc); + case SessionCommand::REPLAY_LIST: return cmdReplayList(cmd.GetExtension(Command_ReplayList::ext), rc); + case SessionCommand::REPLAY_DOWNLOAD: return cmdReplayDownload(cmd.GetExtension(Command_ReplayDownload::ext), rc); + case SessionCommand::REPLAY_MODIFY_MATCH: return cmdReplayModifyMatch(cmd.GetExtension(Command_ReplayModifyMatch::ext), rc); + case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc); + case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break; + case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break; + + case SessionCommand::ACCOUNT_EDIT: return cmdAccountEdit(cmd.GetExtension(Command_AccountEdit::ext), rc); + case SessionCommand::ACCOUNT_IMAGE: return cmdAccountImage(cmd.GetExtension(Command_AccountImage::ext), rc); + case SessionCommand::ACCOUNT_PASSWORD: return cmdAccountPassword(cmd.GetExtension(Command_AccountPassword::ext), rc); + default: return Response::RespFunctionNotAllowed; } } -Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCommand(int cmdType, - const ModeratorCommand &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc) { - switch ((ModeratorCommand::ModeratorCommandType)cmdType) { - case ModeratorCommand::BAN_FROM_SERVER: - return cmdBanFromServer(cmd.GetExtension(Command_BanFromServer::ext), rc); - case ModeratorCommand::BAN_HISTORY: - return cmdGetBanHistory(cmd.GetExtension(Command_GetBanHistory::ext), rc); - case ModeratorCommand::WARN_USER: - return cmdWarnUser(cmd.GetExtension(Command_WarnUser::ext), rc); - case ModeratorCommand::WARN_HISTORY: - return cmdGetWarnHistory(cmd.GetExtension(Command_GetWarnHistory::ext), rc); - case ModeratorCommand::WARN_LIST: - return cmdGetWarnList(cmd.GetExtension(Command_GetWarnList::ext), rc); - case ModeratorCommand::VIEWLOG_HISTORY: - return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc); - case ModeratorCommand::GRANT_REPLAY_ACCESS: - return cmdGrantReplayAccess(cmd.GetExtension(Command_GrantReplayAccess::ext), rc); - case ModeratorCommand::FORCE_ACTIVATE_USER: - return cmdForceActivateUser(cmd.GetExtension(Command_ForceActivateUser::ext), rc); - case ModeratorCommand::GET_ADMIN_NOTES: - return cmdGetAdminNotes(cmd.GetExtension(Command_GetAdminNotes::ext), rc); - case ModeratorCommand::UPDATE_ADMIN_NOTES: - return cmdUpdateAdminNotes(cmd.GetExtension(Command_UpdateAdminNotes::ext), rc); - default: - return Response::RespFunctionNotAllowed; + switch ((ModeratorCommand::ModeratorCommandType) cmdType) { + case ModeratorCommand::BAN_FROM_SERVER: return cmdBanFromServer(cmd.GetExtension(Command_BanFromServer::ext), rc); + case ModeratorCommand::BAN_HISTORY: return cmdGetBanHistory(cmd.GetExtension(Command_GetBanHistory::ext), rc); + case ModeratorCommand::WARN_USER: return cmdWarnUser(cmd.GetExtension(Command_WarnUser::ext), rc); + case ModeratorCommand::WARN_HISTORY: return cmdGetWarnHistory(cmd.GetExtension(Command_GetWarnHistory::ext), rc); + case ModeratorCommand::WARN_LIST: return cmdGetWarnList(cmd.GetExtension(Command_GetWarnList::ext), rc); + case ModeratorCommand::VIEWLOG_HISTORY: return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc); + default: return Response::RespFunctionNotAllowed; } } -Response::ResponseCode -AbstractServerSocketInterface::processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc) { - switch ((AdminCommand::AdminCommandType)cmdType) { - case AdminCommand::SHUTDOWN_SERVER: - return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc); - case AdminCommand::UPDATE_SERVER_MESSAGE: - return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc); - case AdminCommand::RELOAD_CONFIG: - return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc); - case AdminCommand::ADJUST_MOD: - return cmdAdjustMod(cmd.GetExtension(Command_AdjustMod::ext), rc); - default: - return Response::RespFunctionNotAllowed; + switch ((AdminCommand::AdminCommandType) cmdType) { + case AdminCommand::SHUTDOWN_SERVER: return cmdShutdownServer(cmd.GetExtension(Command_ShutdownServer::ext), rc); + case AdminCommand::UPDATE_SERVER_MESSAGE: return cmdUpdateServerMessage(cmd.GetExtension(Command_UpdateServerMessage::ext), rc); + case AdminCommand::RELOAD_CONFIG: return cmdReloadConfig(cmd.GetExtension(Command_ReloadConfig::ext), rc); + case AdminCommand::ADJUST_MOD: return cmdAdjustMod(cmd.GetExtension(Command_AdjustMod::ext), rc); + default: return Response::RespFunctionNotAllowed; } } -Response::ResponseCode AbstractServerSocketInterface::cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - QString list = nameFromStdString(cmd.list()); - QString user = nameFromStdString(cmd.user_name()); + QString list = QString::fromStdString(cmd.list()); + QString user = QString::fromStdString(cmd.user_name()); if ((list != "buddy") && (list != "ignore")) return Response::RespContextError; @@ -293,8 +353,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAddToList(const Command if (id1 == id2) return Response::RespContextError; - QSqlQuery *query = - sqlInterface->prepareQuery("insert into {prefix}_" + list + "list (id_user1, id_user2) values(:id1, :id2)"); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_" + list + "list (id_user1, id_user2) values(:id1, :id2)"); query->bindValue(":id1", id1); query->bindValue(":id2", id2); if (!sqlInterface->execSqlQuery(query)) @@ -308,14 +367,13 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAddToList(const Command return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdRemoveFromList(const Command_RemoveFromList &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - QString list = nameFromStdString(cmd.list()); - QString user = nameFromStdString(cmd.user_name()); + QString list = QString::fromStdString(cmd.list()); + QString user = QString::fromStdString(cmd.user_name()); if ((list != "buddy") && (list != "ignore")) return Response::RespContextError; @@ -332,8 +390,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveFromList(const Co if (id2 < 0) return Response::RespNameNotFound; - QSqlQuery *query = - sqlInterface->prepareQuery("delete from {prefix}_" + list + "list where id_user1 = :id1 and id_user2 = :id2"); + QSqlQuery *query = sqlInterface->prepareQuery("delete from {prefix}_" + list + "list where id_user1 = :id1 and id_user2 = :id2"); query->bindValue(":id1", id1); query->bindValue(":id2", id2); if (!sqlInterface->execSqlQuery(query)) @@ -347,15 +404,14 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRemoveFromList(const Co return Response::RespOk; } -int AbstractServerSocketInterface::getDeckPathId(int basePathId, QStringList path) +int ServerSocketInterface::getDeckPathId(int basePathId, QStringList path) { if (path.isEmpty()) return 0; if (path[0].isEmpty()) return 0; - QSqlQuery *query = sqlInterface->prepareQuery("select id from {prefix}_decklist_folders where id_parent = " - ":id_parent and name = :name and id_user = :id_user"); + QSqlQuery *query = sqlInterface->prepareQuery("select id from {prefix}_decklist_folders where id_parent = :id_parent and name = :name and id_user = :id_user"); query->bindValue(":id_parent", basePathId); query->bindValue(":name", path.takeFirst()); query->bindValue(":id_user", userInfo->id()); @@ -370,25 +426,25 @@ int AbstractServerSocketInterface::getDeckPathId(int basePathId, QStringList pat return getDeckPathId(id, path); } -int AbstractServerSocketInterface::getDeckPathId(const QString &path) +int ServerSocketInterface::getDeckPathId(const QString &path) { return getDeckPathId(0, path.split("/")); } -bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder) +bool ServerSocketInterface::deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder) { - QSqlQuery *query = sqlInterface->prepareQuery( - "select id, name from {prefix}_decklist_folders where id_parent = :id_parent and id_user = :id_user"); + QSqlQuery *query = sqlInterface->prepareQuery("select id, name from {prefix}_decklist_folders where id_parent = :id_parent and id_user = :id_user"); query->bindValue(":id_parent", folderId); query->bindValue(":id_user", userInfo->id()); if (!sqlInterface->execSqlQuery(query)) return false; QMap results; - while (query->next()) + while(query->next()) results[query->value(0).toInt()] = query->value(1).toString(); - for (int key : results.keys()) { + foreach(int key, results.keys()) + { ServerInfo_DeckStorage_TreeItem *newItem = folder->add_items(); newItem->set_id(key); newItem->set_name(results.value(key).toStdString()); @@ -397,8 +453,7 @@ bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_Deck return false; } - query = sqlInterface->prepareQuery("select id, name, upload_time from {prefix}_decklist_files where id_folder = " - ":id_folder and id_user = :id_user"); + query = sqlInterface->prepareQuery("select id, name, upload_time from {prefix}_decklist_files where id_folder = :id_folder and id_user = :id_user"); query->bindValue(":id_folder", folderId); query->bindValue(":id_user", userInfo->id()); if (!sqlInterface->execSqlQuery(query)) @@ -410,7 +465,7 @@ bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_Deck newItem->set_name(query->value(1).toString().toStdString()); ServerInfo_DeckStorage_File *newFile = newItem->mutable_file(); - newFile->set_creation_time(query->value(2).toDateTime().toSecsSinceEpoch()); + newFile->set_creation_time(query->value(2).toDateTime().toTime_t()); } return true; @@ -419,8 +474,7 @@ bool AbstractServerSocketInterface::deckListHelper(int folderId, ServerInfo_Deck // CHECK AUTHENTICATION! // Also check for every function that data belonging to other users cannot be accessed. -Response::ResponseCode AbstractServerSocketInterface::cmdDeckList(const Command_DeckList & /*cmd*/, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdDeckList(const Command_DeckList & /*cmd*/, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; @@ -437,38 +491,30 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckList(const Command_ return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdDeckNewDir(const Command_DeckNewDir &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer & /*rc*/) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; sqlInterface->checkSql(); - QString path = nameFromStdString(cmd.path()); - int folderId = getDeckPathId(path); + int folderId = getDeckPathId(QString::fromStdString(cmd.path())); if (folderId == -1) return Response::RespNameNotFound; - QString name = nameFromStdString(cmd.dir_name()); - if (path.length() + name.length() + 1 > MAX_NAME_LENGTH) - return Response::RespContextError; // do not allow creation of paths that would be too long to delete - - QSqlQuery *query = sqlInterface->prepareQuery( - "insert into {prefix}_decklist_folders (id_parent, id_user, name) values(:id_parent, :id_user, :name)"); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_decklist_folders (id_parent, id_user, name) values(:id_parent, :id_user, :name)"); query->bindValue(":id_parent", folderId); query->bindValue(":id_user", userInfo->id()); - query->bindValue(":name", name); + query->bindValue(":name", QString::fromStdString(cmd.dir_name())); if (!sqlInterface->execSqlQuery(query)) return Response::RespContextError; return Response::RespOk; } -void AbstractServerSocketInterface::deckDelDirHelper(int basePathId) +void ServerSocketInterface::deckDelDirHelper(int basePathId) { sqlInterface->checkSql(); - QSqlQuery *query = - sqlInterface->prepareQuery("select id from {prefix}_decklist_folders where id_parent = :id_parent"); + QSqlQuery *query = sqlInterface->prepareQuery("select id from {prefix}_decklist_folders where id_parent = :id_parent"); query->bindValue(":id_parent", basePathId); sqlInterface->execSqlQuery(query); while (query->next()) @@ -483,10 +529,9 @@ void AbstractServerSocketInterface::deckDelDirHelper(int basePathId) sqlInterface->execSqlQuery(query); } -void AbstractServerSocketInterface::sendServerMessage(const QString userName, const QString message) +void ServerSocketInterface::sendServerMessage(const QString userName, const QString message) { - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); if (!user) return; @@ -499,29 +544,27 @@ void AbstractServerSocketInterface::sendServerMessage(const QString userName, co delete se; } -Response::ResponseCode AbstractServerSocketInterface::cmdDeckDelDir(const Command_DeckDelDir &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer & /*rc*/) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; sqlInterface->checkSql(); - int basePathId = getDeckPathId(nameFromStdString(cmd.path())); + int basePathId = getDeckPathId(QString::fromStdString(cmd.path())); if ((basePathId == -1) || (basePathId == 0)) return Response::RespNameNotFound; deckDelDirHelper(basePathId); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer & /*rc*/) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; sqlInterface->checkSql(); - QSqlQuery *query = - sqlInterface->prepareQuery("select id from {prefix}_decklist_files where id = :id and id_user = :id_user"); + QSqlQuery *query = sqlInterface->prepareQuery("select id from {prefix}_decklist_files where id = :id and id_user = :id_user"); query->bindValue(":id", cmd.deck_id()); query->bindValue(":id_user", userInfo->id()); sqlInterface->execSqlQuery(query); @@ -535,8 +578,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckDel(const Command_D return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Command_DeckUpload &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; @@ -546,23 +588,19 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman sqlInterface->checkSql(); - QString deckStr = fileFromStdString(cmd.deck_list()); - DeckList deck; - if (!deck.loadFromString_Native(deckStr)) - return Response::RespContextError; + QString deckStr = QString::fromStdString(cmd.deck_list()); + DeckList deck(deckStr); QString deckName = deck.getName(); if (deckName.isEmpty()) deckName = "Unnamed deck"; if (cmd.has_path()) { - int folderId = getDeckPathId(nameFromStdString(cmd.path())); + int folderId = getDeckPathId(QString::fromStdString(cmd.path())); if (folderId == -1) return Response::RespNameNotFound; - QSqlQuery *query = - sqlInterface->prepareQuery("insert into {prefix}_decklist_files (id_folder, id_user, name, upload_time, " - "content) values(:id_folder, :id_user, :name, NOW(), :content)"); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_decklist_files (id_folder, id_user, name, upload_time, content) values(:id_folder, :id_user, :name, NOW(), :content)"); query->bindValue(":id_folder", folderId); query->bindValue(":id_user", userInfo->id()); query->bindValue(":name", deckName); @@ -573,12 +611,10 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); fileInfo->set_id(query->lastInsertId().toInt()); fileInfo->set_name(deckName.toStdString()); - fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toSecsSinceEpoch()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); rc.setResponseExtension(re); } else if (cmd.has_deck_id()) { - QSqlQuery *query = - sqlInterface->prepareQuery("update {prefix}_decklist_files set name=:name, upload_time=NOW(), " - "content=:content where id = :id_deck and id_user = :id_user"); + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_decklist_files set name=:name, upload_time=NOW(), content=:content where id = :id_deck and id_user = :id_user"); query->bindValue(":id_deck", cmd.deck_id()); query->bindValue(":id_user", userInfo->id()); query->bindValue(":name", deckName); @@ -592,7 +628,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file(); fileInfo->set_id(cmd.deck_id()); fileInfo->set_name(deckName.toStdString()); - fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toSecsSinceEpoch()); + fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t()); rc.setResponseExtension(re); } else return Response::RespInvalidData; @@ -600,8 +636,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckUpload(const Comman return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdDeckDownload(const Command_DeckDownload &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; @@ -609,7 +644,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckDownload(const Comm DeckList *deck; try { deck = sqlInterface->getDeckFromDatabase(cmd.deck_id(), userInfo->id()); - } catch (Response::ResponseCode &r) { + } catch(Response::ResponseCode r) { return r; } @@ -621,18 +656,14 @@ Response::ResponseCode AbstractServerSocketInterface::cmdDeckDownload(const Comm return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Command_ReplayList & /*cmd*/, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_ReplayList & /*cmd*/, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; Response_ReplayList *re = new Response_ReplayList; - QSqlQuery *query1 = sqlInterface->prepareQuery( - "select a.id_game, a.replay_name, b.room_name, b.time_started, b.time_finished, b.descr, a.do_not_hide from " - "{prefix}_replays_access a left join {prefix}_games b on b.id = a.id_game where a.id_player = :id_player and " - "(a.do_not_hide = 1 or date_add(b.time_started, interval 7 day) > now())"); + QSqlQuery *query1 = sqlInterface->prepareQuery("select a.id_game, a.replay_name, b.room_name, b.time_started, b.time_finished, b.descr, a.do_not_hide from {prefix}_replays_access a left join {prefix}_games b on b.id = a.id_game where a.id_player = :id_player and (a.do_not_hide = 1 or date_add(b.time_started, interval 7 day) > now())"); query1->bindValue(":id_player", userInfo->id()); sqlInterface->execSqlQuery(query1); while (query1->next()) { @@ -641,8 +672,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Comman const int gameId = query1->value(0).toInt(); matchInfo->set_game_id(gameId); matchInfo->set_room_name(query1->value(2).toString().toStdString()); - const int timeStarted = query1->value(3).toDateTime().toSecsSinceEpoch(); - const int timeFinished = query1->value(4).toDateTime().toSecsSinceEpoch(); + const int timeStarted = query1->value(3).toDateTime().toTime_t(); + const int timeFinished = query1->value(4).toDateTime().toTime_t(); matchInfo->set_time_started(timeStarted); matchInfo->set_length(timeFinished - timeStarted); matchInfo->set_game_name(query1->value(5).toString().toStdString()); @@ -650,16 +681,14 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Comman matchInfo->set_do_not_hide(query1->value(6).toBool()); { - QSqlQuery *query2 = - sqlInterface->prepareQuery("select player_name from {prefix}_games_players where id_game = :id_game"); + QSqlQuery *query2 = sqlInterface->prepareQuery("select player_name from {prefix}_games_players where id_game = :id_game"); query2->bindValue(":id_game", gameId); sqlInterface->execSqlQuery(query2); while (query2->next()) matchInfo->add_player_names(query2->value(0).toString().toStdString()); } { - QSqlQuery *query3 = - sqlInterface->prepareQuery("select id, duration from {prefix}_replays where id_game = :id_game"); + QSqlQuery *query3 = sqlInterface->prepareQuery("select id, duration from {prefix}_replays where id_game = :id_game"); query3->bindValue(":id_game", gameId); sqlInterface->execSqlQuery(query3); while (query3->next()) { @@ -675,16 +704,13 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayList(const Comman return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReplayDownload(const Command_ReplayDownload &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; { - QSqlQuery *query = - sqlInterface->prepareQuery("select 1 from {prefix}_replays_access a left join {prefix}_replays b on " - "a.id_game = b.id_game where b.id = :id_replay and a.id_player = :id_player"); + QSqlQuery *query = sqlInterface->prepareQuery("select 1 from {prefix}_replays_access a left join {prefix}_replays b on a.id_game = b.id_game where b.id = :id_replay and a.id_player = :id_player"); query->bindValue(":id_replay", cmd.replay_id()); query->bindValue(":id_player", userInfo->id()); if (!sqlInterface->execSqlQuery(query)) @@ -709,8 +735,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayDownload(const Co return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer & /*rc*/) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; @@ -718,8 +743,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayModifyMatch(const if (!sqlInterface->checkSql()) return Response::RespInternalError; - QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_replays_access set do_not_hide=:do_not_hide where " - "id_player = :id_player and id_game = :id_game"); + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_replays_access set do_not_hide=:do_not_hide where id_player = :id_player and id_game = :id_game"); query->bindValue(":id_player", userInfo->id()); query->bindValue(":id_game", cmd.game_id()); query->bindValue(":do_not_hide", cmd.do_not_hide()); @@ -729,8 +753,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayModifyMatch(const return query->numRowsAffected() > 0 ? Response::RespOk : Response::RespNameNotFound; } -Response::ResponseCode AbstractServerSocketInterface::cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer & /*rc*/) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; @@ -738,8 +761,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayDeleteMatch(const if (!sqlInterface->checkSql()) return Response::RespInternalError; - QSqlQuery *query = sqlInterface->prepareQuery( - "delete from {prefix}_replays_access where id_player = :id_player and id_game = :id_game"); + QSqlQuery *query = sqlInterface->prepareQuery("delete from {prefix}_replays_access where id_player = :id_player and id_game = :id_game"); query->bindValue(":id_player", userInfo->id()); query->bindValue(":id_game", cmd.game_id()); @@ -748,157 +770,28 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReplayDeleteMatch(const return query->numRowsAffected() > 0 ? Response::RespOk : Response::RespNameNotFound; } -/** - * Generates a hash for the given replay folder, used for auth when replay sharing. - * This is a separate function in case we change the hash implementation in the future. - * - * Currently, we append together the first 128 bytes of the first 3 replays in the game. - * Then we md5 hash it, base64 encode it, and truncate the result to 10 characters. - * - * @param gameId The replay match to hash - * @return The hash as a QString. Returns an empty string if failed - */ -QString AbstractServerSocketInterface::createHashForReplay(int gameId) -{ - QSqlQuery *query = - sqlInterface->prepareQuery("select replay from {prefix}_replays where id_game = :id_game limit 3"); - query->bindValue(":id_game", gameId); - - if (!sqlInterface->execSqlQuery(query)) - return ""; - - QByteArray replaysBytes; - while (query->next()) { - QByteArray replay = query->value(0).toByteArray(); - replay.truncate(128); - replaysBytes.append(replay); - } - - auto hash = - QCryptographicHash::hash(replaysBytes, QCryptographicHash::Md5).toBase64(QByteArray::OmitTrailingEquals); - hash.truncate(10); - return hash; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReplayGetCode(const Command_ReplayGetCode &cmd, - ResponseContainer &rc) -{ - if (authState != PasswordRight) - return Response::RespFunctionNotAllowed; - - // Check that user has access to replay match - { - QSqlQuery *query = sqlInterface->prepareQuery( - "select 1 from {prefix}_replays_access where id_game = :id_game and id_player = :id_player"); - query->bindValue(":id_game", cmd.game_id()); - query->bindValue(":id_player", userInfo->id()); - if (!sqlInterface->execSqlQuery(query)) - return Response::RespInternalError; - if (!query->next()) - return Response::RespAccessDenied; - } - - QString hash = createHashForReplay(cmd.game_id()); - if (hash.isEmpty()) { - return Response::RespInternalError; - } - - // code is of the form - - QString code = QString(QString::number(cmd.game_id()) + "-" + hash); - - Response_ReplayGetCode *re = new Response_ReplayGetCode; - re->set_replay_code(code.toStdString()); - rc.setResponseExtension(re); - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd, - ResponseContainer & /*rc*/) -{ - // code is of the form - - QString code = QString::fromStdString(cmd.replay_code()); - QStringList split = code.split("-"); - if (split.size() != 2) { - // always return the same error response if code is incorrect, to not leak info to user - return Response::RespNameNotFound; - } - QString gameId = split[0]; - QString hash = split[1]; - - // Determine if the replay actually exists (and grab the replay name while at it) - auto *replayExistsQuery = - sqlInterface->prepareQuery("select replay_name from {prefix}_replays_access where id_game = :id_game limit 1"); - replayExistsQuery->bindValue(":id_game", gameId); - if (!sqlInterface->execSqlQuery(replayExistsQuery)) { - return Response::RespInternalError; - } - if (!replayExistsQuery->next()) { - return Response::RespNameNotFound; - } - - const auto &replayName = replayExistsQuery->value(0).toString(); - - // Check if hash is correct - if (hash != createHashForReplay(gameId.toInt())) { - return Response::RespNameNotFound; - } - - // Determine if user already has access to replay - auto *alreadyAccessQuery = sqlInterface->prepareQuery( - "select 1 from {prefix}_replays_access where id_game = :id_game and id_player = :id_player"); - alreadyAccessQuery->bindValue(":id_game", gameId); - alreadyAccessQuery->bindValue(":id_player", userInfo->id()); - if (!sqlInterface->execSqlQuery(alreadyAccessQuery)) { - return Response::RespInternalError; - } - if (alreadyAccessQuery->next()) { - return Response::RespOk; - } - - // Grant the User access to the replay - auto *grantReplayAccessQuery = - sqlInterface->prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name, do_not_hide) " - "values(:idgame, :idplayer, :replayname, 0)"); - grantReplayAccessQuery->bindValue(":idgame", gameId); - grantReplayAccessQuery->bindValue(":idplayer", userInfo->id()); - grantReplayAccessQuery->bindValue(":replayname", replayName); - - if (!sqlInterface->execSqlQuery(grantReplayAccessQuery)) { - return Response::RespInternalError; - } - - // update user's view - Event_ReplayAdded event; - SessionEvent *se = prepareSessionEvent(event); - sendProtocolItem(*se); - delete se; - - return Response::RespOk; -} // MODERATOR FUNCTIONS. // May be called by admins and moderators. Permission is checked by the calling function. -Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Command_ViewLogHistory &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc) { QList messageList; - QString userName = nameFromStdString(cmd.user_name()); - QString ipAddress = nameFromStdString(cmd.ip_address()); - QString gameName = nameFromStdString(cmd.game_name()); - QString gameID = nameFromStdString(cmd.game_id()); - QString message = textFromStdString(cmd.message()); + QString userName = QString::fromStdString(cmd.user_name()); + QString ipAddress = QString::fromStdString(cmd.ip_address()); + QString gameName = QString::fromStdString(cmd.game_name()); + QString gameID = QString::fromStdString(cmd.game_id()); + QString message = QString::fromStdString(cmd.message()); bool chatType = false; bool gameType = false; bool roomType = false; for (int i = 0; i != cmd.log_location_size(); ++i) { - if (nameFromStdString(cmd.log_location(i)).simplified() == "room") + if (QString::fromStdString(cmd.log_location(i)).simplified() == "room") roomType = true; - if (nameFromStdString(cmd.log_location(i)).simplified() == "game") + if (QString::fromStdString(cmd.log_location(i)).simplified() == "game") gameType = true; - if (nameFromStdString(cmd.log_location(i)).simplified() == "chat") + if (QString::fromStdString(cmd.log_location(i)).simplified() == "chat") chatType = true; } @@ -906,62 +799,17 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetLogHistory(const Com int maximumResults = cmd.maximum_results(); Response_ViewLogHistory *re = new Response_ViewLogHistory; - - if (servatrice->getEnableLogQuery()) { - QListIterator messageIterator(sqlInterface->getMessageLogHistory( - userName, ipAddress, gameName, gameID, message, chatType, gameType, roomType, dateRange, maximumResults)); - while (messageIterator.hasNext()) - re->add_log_message()->CopyFrom(messageIterator.next()); - } else { - ServerInfo_ChatMessage chatMessage; - - // create dummy chat message for room tab in the event the query is for room messages (and possibly not others) - chatMessage.set_time(QString(tr("Log query disabled, please contact server owner for details.")).toStdString()); - chatMessage.set_sender_id(QString("").toStdString()); - chatMessage.set_sender_name(QString("").toStdString()); - chatMessage.set_sender_ip(QString("").toStdString()); - chatMessage.set_message(QString("").toStdString()); - chatMessage.set_target_type(QString("room").toStdString()); - chatMessage.set_target_id(QString("").toStdString()); - chatMessage.set_target_name(QString("").toStdString()); - messageList << chatMessage; - - // create dummy chat message for room tab in the event the query is for game messages (and possibly not others) - chatMessage.set_time(QString(tr("Log query disabled, please contact server owner for details.")).toStdString()); - chatMessage.set_sender_id(QString("").toStdString()); - chatMessage.set_sender_name(QString("").toStdString()); - chatMessage.set_sender_ip(QString("").toStdString()); - chatMessage.set_message(QString("").toStdString()); - chatMessage.set_target_type(QString("game").toStdString()); - chatMessage.set_target_id(QString("").toStdString()); - chatMessage.set_target_name(QString("").toStdString()); - messageList << chatMessage; - - // create dummy chat message for room tab in the event the query is for chat messages (and possibly not others) - chatMessage.set_time(QString(tr("Log query disabled, please contact server owner for details.")).toStdString()); - chatMessage.set_sender_id(QString("").toStdString()); - chatMessage.set_sender_name(QString("").toStdString()); - chatMessage.set_sender_ip(QString("").toStdString()); - chatMessage.set_message(QString("").toStdString()); - chatMessage.set_target_type(QString("chat").toStdString()); - chatMessage.set_target_id(QString("").toStdString()); - chatMessage.set_target_name(QString("").toStdString()); - messageList << chatMessage; - - QListIterator messageIterator(messageList); - while (messageIterator.hasNext()) - re->add_log_message()->CopyFrom(messageIterator.next()); - } - + QListIterator messageIterator(sqlInterface->getMessageLogHistory(userName,ipAddress,gameName,gameID,message,chatType,gameType,roomType,dateRange,maximumResults)); + while (messageIterator.hasNext()) + re->add_log_message()->CopyFrom(messageIterator.next()); rc.setResponseExtension(re); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdGetBanHistory(const Command_GetBanHistory &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc) { QList banList; - QString userName = nameFromStdString(cmd.user_name()); + QString userName = QString::fromStdString(cmd.user_name()); Response_BanHistory *re = new Response_BanHistory; QListIterator banIterator(sqlInterface->getUserBanHistory(userName)); @@ -971,27 +819,25 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetBanHistory(const Com return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnList(const Command_GetWarnList &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc) { Response_WarnList *re = new Response_WarnList; QString officialWarnings = settingsCache->value("server/officialwarnings").toString(); - QStringList warningsList = officialWarnings.split(",", Qt::SkipEmptyParts); - for (const QString &warning : warningsList) { + QStringList warningsList = officialWarnings.split(",", QString::SkipEmptyParts); + foreach(QString warning, warningsList){ re->add_warning(warning.toStdString()); } - re->set_user_name(nameFromStdString(cmd.user_name()).toStdString()); - re->set_user_clientid(nameFromStdString(cmd.user_clientid()).toStdString()); + re->set_user_name(cmd.user_name()); + re->set_user_clientid(cmd.user_clientid()); rc.setResponseExtension(re); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnHistory(const Command_GetWarnHistory &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc) { QList warnList; - QString userName = nameFromStdString(cmd.user_name()); + QString userName = QString::fromStdString(cmd.user_name()); Response_WarnHistory *re = new Response_WarnHistory; QListIterator warnIterator(sqlInterface->getUserWarnHistory(userName)); @@ -1001,51 +847,27 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnHistory(const Co return Response::RespOk; } -void AbstractServerSocketInterface::removeSaidMessages(const QString &userName, int amount) +Response::ResponseCode ServerSocketInterface::cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer & /*rc*/) { - for (auto *room : rooms.values()) { - room->removeSaidMessages(userName, amount); - } -} - -Response::ResponseCode AbstractServerSocketInterface::cmdWarnUser(const Command_WarnUser &cmd, - ResponseContainer & /*rc*/) -{ - if (!sqlInterface->checkSql()) { // sql database is required, without database there are no moderators anyway + if (!sqlInterface->checkSql()) return Response::RespInternalError; - } - QString userName = nameFromStdString(cmd.user_name()).simplified(); - QString warningReason = textFromStdString(cmd.reason()).simplified(); - QString clientId = nameFromStdString(cmd.clientid()).simplified(); + QString userName = QString::fromStdString(cmd.user_name()).simplified(); + QString warningReason = QString::fromStdString(cmd.reason()).simplified(); + QString clientID = QString::fromStdString(cmd.clientid()).simplified(); QString sendingModerator = QString::fromStdString(userInfo->name()).simplified(); - int amountRemove = cmd.remove_messages(); - if (amountRemove != 0) { - removeSaidMessages(userName, amountRemove); - } - if (sqlInterface->addWarning(userName, sendingModerator, warningReason, clientId)) { - servatrice->clientsLock.lockForRead(); - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); - QList moderatorList = server->getOnlineModeratorList(); - servatrice->clientsLock.unlock(); - - if (user != nullptr) { + if (sqlInterface->addWarning(userName, sendingModerator, warningReason, clientID)) { + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { Event_NotifyUser event; event.set_type(Event_NotifyUser::WARNING); - event.set_warning_reason(warningReason.toStdString()); + event.set_warning_reason(cmd.reason()); SessionEvent *se = user->prepareSessionEvent(event); user->sendProtocolItem(*se); delete se; } - for (QString &moderator : moderatorList) { - QString notificationMessage = sendingModerator + " has sent a warning with the following information"; - notificationMessage.append("\n Username: " + userName); - notificationMessage.append("\n Reason: " + warningReason); - sendServerMessage(moderator.simplified(), notificationMessage); - } return Response::RespOk; } else { @@ -1053,78 +875,65 @@ Response::ResponseCode AbstractServerSocketInterface::cmdWarnUser(const Command_ } } -Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Command_BanFromServer &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer & /*rc*/) { if (!sqlInterface->checkSql()) return Response::RespInternalError; - QString userName = nameFromStdString(cmd.user_name()).simplified(); - QString address = nameFromStdString(cmd.address()).simplified(); - QString clientId = nameFromStdString(cmd.clientid()).simplified(); - QString visibleReason = textFromStdString(cmd.visible_reason()); + QString userName = QString::fromStdString(cmd.user_name()).simplified(); + QString address = QString::fromStdString(cmd.address()).simplified(); + QString clientID = QString::fromStdString(cmd.clientid()).simplified(); - if (userName.isEmpty() && address.isEmpty() && clientId.isEmpty()) + if (userName.isEmpty() && address.isEmpty() && clientID.isEmpty()) return Response::RespOk; - int amountRemove = cmd.remove_messages(); - if (amountRemove != 0) { - removeSaidMessages(userName, amountRemove); - } - QString trustedSources = settingsCache->value("server/trusted_sources", "127.0.0.1,::1").toString(); + QString trustedSources = settingsCache->value("server/trusted_sources","127.0.0.1,::1").toString(); int minutes = cmd.minutes(); - if (trustedSources.contains(address, Qt::CaseInsensitive)) + if (trustedSources.contains(address,Qt::CaseInsensitive)) address = ""; - QSqlQuery *query = sqlInterface->prepareQuery( - "insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason, " - "clientid) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason, :client_id)"); + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason, clientid) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason, :client_id)"); query->bindValue(":user_name", userName); query->bindValue(":ip_address", address); query->bindValue(":id_admin", userInfo->id()); query->bindValue(":minutes", minutes); - query->bindValue(":reason", textFromStdString(cmd.reason())); - query->bindValue(":visible_reason", visibleReason); - query->bindValue(":client_id", nameFromStdString(cmd.clientid())); + query->bindValue(":reason", QString::fromStdString(cmd.reason())); + query->bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason())); + query->bindValue(":client_id", QString::fromStdString(cmd.clientid())); sqlInterface->execSqlQuery(query); servatrice->clientsLock.lockForRead(); - QList moderatorList = server->getOnlineModeratorList(); - QList userList = servatrice->getUsersWithAddressAsList(QHostAddress(address)); + QList userList = servatrice->getUsersWithAddressAsList(QHostAddress(address)); if (!userName.isEmpty()) { - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); if (user && !userList.contains(user)) userList.append(user); } - if (userName.isEmpty() && address.isEmpty() && (!clientId.isEmpty())) { - QSqlQuery *clientIdQuery = - sqlInterface->prepareQuery("select name from {prefix}_users where clientid = :client_id"); - clientIdQuery->bindValue(":client_id", nameFromStdString(cmd.clientid())); - sqlInterface->execSqlQuery(clientIdQuery); - if (!sqlInterface->execSqlQuery(clientIdQuery)) { - qCWarning(AbstractServerSocketInterfaceLog) << "ClientID username ban lookup failed: SQL Error"; + if (userName.isEmpty() && address.isEmpty() && (!clientID.isEmpty())) { + QSqlQuery *query = sqlInterface->prepareQuery("select name from {prefix}_users where clientid = :client_id"); + query->bindValue(":client_id", QString::fromStdString(cmd.clientid())); + sqlInterface->execSqlQuery(query); + if (!sqlInterface->execSqlQuery(query)){ + qDebug("ClientID username ban lookup failed: SQL Error"); } else { - while (clientIdQuery->next()) { - userName = clientIdQuery->value(0).toString(); - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); + while (query->next()) { + userName = query->value(0).toString(); + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); if (user && !userList.contains(user)) - userList.append(user); + userList.append(user); } } } - servatrice->clientsLock.unlock(); if (!userList.isEmpty()) { Event_ConnectionClosed event; event.set_reason(Event_ConnectionClosed::BANNED); if (cmd.has_visible_reason()) - event.set_reason_str(visibleReason.toStdString()); + event.set_reason_str(cmd.visible_reason()); if (minutes) - event.set_end_time(QDateTime::currentDateTime().addSecs(60 * minutes).toSecsSinceEpoch()); + event.set_end_time(QDateTime::currentDateTime().addSecs(60 * minutes).toTime_t()); for (int i = 0; i < userList.size(); ++i) { SessionEvent *se = userList[i]->prepareSessionEvent(event); userList[i]->sendProtocolItem(*se); @@ -1132,323 +941,150 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com QMetaObject::invokeMethod(userList[i], "prepareDestroy", Qt::QueuedConnection); } } - - for (QString &moderator : moderatorList) { - QString notificationMessage = - QString::fromStdString(userInfo->name()).simplified() + " has placed a ban with the following information"; - if (!userName.isEmpty()) - notificationMessage.append("\n Username: " + userName); - if (!address.isEmpty()) - notificationMessage.append("\n IP Address: " + address); - if (!clientId.isEmpty()) - notificationMessage.append("\n Client ID: " + clientId); - - notificationMessage.append("\n Length: " + QString::number(minutes) + " minute(s)"); - notificationMessage.append("\n Internal Reason: " + textFromStdString(cmd.reason())); - notificationMessage.append("\n Visible Reason: " + textFromStdString(cmd.visible_reason())); - sendServerMessage(moderator.simplified(), notificationMessage); - } + servatrice->clientsLock.unlock(); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, - ResponseContainer &rc) +Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) { - QString userName = nameFromStdString(cmd.user_name()); - QString clientId = nameFromStdString(cmd.clientid()); - qCDebug(AbstractServerSocketInterfaceLog) << "Got register command for user:" << userName; + QString userName = QString::fromStdString(cmd.user_name()); + QString clientId = QString::fromStdString(cmd.clientid()); + qDebug() << "Got register command: " << userName; bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); - if (!registrationEnabled) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Server functionality disabled", false); - + if (!registrationEnabled) return Response::RespRegistrationDisabled; - } - - const QString emailBlackList = servatrice->getEmailBlackList(); - const QString emailWhiteList = servatrice->getEmailWhiteList(); - const auto parsedEmailParts = EmailParser::parseEmailAddress(nameFromStdString(cmd.email())); - const auto emailUser = parsedEmailParts.first; - const auto emailDomain = parsedEmailParts.second; - const QStringList emailBlackListFilters = emailBlackList.split(",", Qt::SkipEmptyParts); - const QStringList emailWhiteListFilters = emailWhiteList.split(",", Qt::SkipEmptyParts); + QString emailAddress = QString::fromStdString(cmd.email()); bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); - if (requireEmailForRegistration && emailUser.isEmpty()) { - return Response::RespEmailRequiredToRegister; + if (requireEmailForRegistration) + { + QRegExp rx("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b"); + if(emailAddress.isEmpty() || !rx.exactMatch(emailAddress)) + return Response::RespEmailRequiredToRegister; } - // If a whitelist exists, ensure the email address domain IS in the whitelist - if (!emailWhiteListFilters.isEmpty() && !emailWhiteListFilters.contains(emailDomain, Qt::CaseInsensitive)) { - if (servatrice->getEnableRegistrationAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Email used is not whitelisted", false); - } - auto *re = new Response_Register; - re->set_denied_reason_str( - "The email address provider used during registration has not been approved for use on this server."); - rc.setResponseExtension(re); - return Response::RespEmailBlackListed; - } - - // If a blacklist exists, ensure the email address domain is NOT in the blacklist - if (!emailBlackListFilters.isEmpty() && emailBlackListFilters.contains(emailDomain, Qt::CaseInsensitive)) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Email used is blacklisted", false); - - return Response::RespEmailBlackListed; - } - - //! \todo Move this method outside of the db interface + // TODO: Move this method outside of the db interface QString errorString; - if (!sqlInterface->usernameIsValid(userName, errorString)) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Username is invalid", false); - + if (!sqlInterface->usernameIsValid(userName, errorString)) + { Response_Register *re = new Response_Register; re->set_denied_reason_str(errorString.toStdString()); rc.setResponseExtension(re); return Response::RespUsernameInvalid; } - if (userName.toLower().simplified() == "servatrice") { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Username is invalid", false); - + if (userName.toLower().simplified() == "servatrice") return Response::RespUsernameInvalid; - } - - if (sqlInterface->userExists(userName)) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Username already exists", false); + if(sqlInterface->userExists(userName)) return Response::RespUserAlreadyExists; - } - - const auto parsedEmailAddress = EmailParser::getParsedEmailAddress(parsedEmailParts); - if (servatrice->getMaxAccountsPerEmail() > 0 && - sqlInterface->checkNumberOfUserAccounts(parsedEmailAddress) >= servatrice->getMaxAccountsPerEmail()) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Too many usernames registered with this email address", - false); - - return Response::RespTooManyRequests; - } QString banReason; int banSecondsRemaining; - if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banSecondsRemaining)) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "User is banned", false); - + if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banSecondsRemaining)) + { Response_Register *re = new Response_Register; re->set_denied_reason_str(banReason.toStdString()); if (banSecondsRemaining != 0) - re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toSecsSinceEpoch()); + re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t()); rc.setResponseExtension(re); return Response::RespUserIsBanned; } - if (tooManyRegistrationAttempts(this->getAddress())) { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Too many registration attempts from this ip address", - false); - + if (tooManyRegistrationAttempts(this->getAddress())) return Response::RespTooManyRequests; - } - QString realName = nameFromStdString(cmd.real_name()); - QString country = nameFromStdString(cmd.country()); - QString password; - bool passwordNeedsHash = false; - if (cmd.has_password()) { - if (cmd.password().length() > MAX_NAME_LENGTH) - return Response::RespRegistrationFailed; - password = QString::fromStdString(cmd.password()); - passwordNeedsHash = true; - if (!isPasswordLongEnough(password.length())) { - if (servatrice->getEnableRegistrationAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Password is too short", false); - } - return Response::RespPasswordTooShort; - } - } else if (cmd.hashed_password().length() > MAX_NAME_LENGTH) { - return Response::RespRegistrationFailed; - } else { - password = QString::fromStdString(cmd.hashed_password()); - } + QString realName = QString::fromStdString(cmd.real_name()); + ServerInfo_User_Gender gender = cmd.gender(); + QString country = QString::fromStdString(cmd.country()); + QString password = QString::fromStdString(cmd.password()); - bool requireEmailActivation = settingsCache->value("registration/requireemailactivation", true).toBool(); - bool regSucceeded = sqlInterface->registerUser(userName, realName, password, passwordNeedsHash, parsedEmailAddress, - country, !requireEmailActivation); + // TODO make this configurable? + if(password.length() < 6) + return Response::RespPasswordTooShort; - if (regSucceeded) { - qCDebug(AbstractServerSocketInterfaceLog) << "Accepted register command for user:" << userName; - if (requireEmailActivation) { - QSqlQuery *query = - sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)"); + QString token; + bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country, token, !requireEmailForRegistration); + + if(regSucceeded) + { + qDebug() << "Accepted register command for user: " << userName; + if(requireEmailForRegistration) + { + QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)"); query->bindValue(":name", userName); if (!sqlInterface->execSqlQuery(query)) return Response::RespRegistrationFailed; - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "", true); - return Response::RespRegistrationAcceptedNeedsActivation; } else { - - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "", true); - return Response::RespRegistrationAccepted; } } else { - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "REGISTER_ACCOUNT", "Unknown reason for failure", false); - return Response::RespRegistrationFailed; } } -bool AbstractServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress) +bool ServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress) { - //! \todo implement - Q_UNUSED(ipAddress); + // TODO: implement + Q_UNUSED(ipAddress); return false; } -Response::ResponseCode AbstractServerSocketInterface::cmdActivateAccount(const Command_Activate &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /*rc*/) { - QString userName = nameFromStdString(cmd.user_name()); - QString token = nameFromStdString(cmd.token()); - QString clientId = nameFromStdString(cmd.clientid()); - - if (clientId.isEmpty()) - clientId = "UNKNOWN"; - - if (sqlInterface->activateUser(userName, token)) { - qCDebug(AbstractServerSocketInterfaceLog) << "Accepted activation for user" << userName; - - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "ACTIVATE_ACCOUNT", "", true); + QString userName = QString::fromStdString(cmd.user_name()); + QString token = QString::fromStdString(cmd.token()); + if(sqlInterface->activateUser(userName, token)) + { + qDebug() << "Accepted activation for user" << QString::fromStdString(cmd.user_name()); return Response::RespActivationAccepted; } else { - qCDebug(AbstractServerSocketInterfaceLog) << "Failed activation for user" << userName; - - if (servatrice->getEnableRegistrationAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "ACTIVATE_ACCOUNT", "Failed to activate account, incorrect activation token", - false); - + qDebug() << "Failed activation for user" << QString::fromStdString(cmd.user_name()); return Response::RespActivationFailed; } } -Response::ResponseCode AbstractServerSocketInterface::cmdAccountEdit(const Command_AccountEdit &cmd, - ResponseContainer & /* rc */) +Response::ResponseCode ServerSocketInterface::cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer & /* rc */) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - QString realName = nameFromStdString(cmd.real_name()); - const auto parsedEmailAddress = EmailParser::getParsedEmailAddress(nameFromStdString(cmd.email())); - QString country = nameFromStdString(cmd.country()); + QString realName = QString::fromStdString(cmd.real_name()); + QString emailAddress = QString::fromStdString(cmd.email()); + ServerInfo_User_Gender gender = cmd.gender(); + QString country = QString::fromStdString(cmd.country()); - bool checkedPassword = false; QString userName = QString::fromStdString(userInfo->name()); - if (cmd.has_password_check()) { - if (cmd.password_check().length() > MAX_NAME_LENGTH) - return Response::RespWrongPassword; - QString password = QString::fromStdString(cmd.password_check()); - QString clientId = QString::fromStdString(userInfo->clientid()); - QString reasonStr{}; - int secondsLeft{}; - AuthenticationResult checkStatus = - databaseInterface->checkUserPassword(this, userName, password, clientId, reasonStr, secondsLeft, true); - if (checkStatus == PasswordRight) { - checkedPassword = true; - } else { - // the user already logged in with this info, the only change is their password - return Response::RespWrongPassword; - } - } - QStringList queryList({}); - if (cmd.has_real_name()) { - queryList << "realname=:realName"; - } - if (cmd.has_email()) { - // a real password is required in order to change the email address - if (usingRealPassword || checkedPassword) { - queryList << "email=:email"; - } else { - return Response::RespFunctionNotAllowed; - } - } - if (cmd.has_country()) { - queryList << "country=:country"; - } - if (queryList.isEmpty()) - return Response::RespOk; - - QString queryText = QString("update {prefix}_users set %1 where name=:userName").arg(queryList.join(", ")); - QSqlQuery *query = sqlInterface->prepareQuery(queryText); - if (cmd.has_real_name()) { - auto _realName = nameFromStdString(cmd.real_name()); - query->bindValue(":realName", _realName); - } - if (cmd.has_email()) { - const auto _parsedEmailAddress = EmailParser::getParsedEmailAddress(nameFromStdString(cmd.email())); - query->bindValue(":email", _parsedEmailAddress); - } - if (cmd.has_country()) { - auto _country = nameFromStdString(cmd.country()); - query->bindValue(":country", _country); - } + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set realname=:realName, email=:email, gender=:gender, country=:country where name=:userName"); + query->bindValue(":realName", realName); + query->bindValue(":email", emailAddress); + query->bindValue(":gender", sqlInterface->getGenderChar(gender)); + query->bindValue(":country", country); query->bindValue(":userName", userName); - if (!sqlInterface->execSqlQuery(query)) return Response::RespInternalError; - if (cmd.has_real_name()) { - userInfo->set_real_name(realName.toStdString()); - } - if (cmd.has_email()) { - userInfo->set_email(parsedEmailAddress.toStdString()); - } - if (cmd.has_country()) { - userInfo->set_country(country.toStdString()); - } + userInfo->set_real_name(cmd.real_name()); + userInfo->set_email(cmd.email()); + userInfo->set_gender(cmd.gender()); + userInfo->set_country(cmd.country()); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Command_AccountImage &cmd, - ResponseContainer & /* rc */) +Response::ResponseCode ServerSocketInterface::cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer & /* rc */) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - size_t length = qMin(cmd.image().length(), (size_t)MAX_FILE_LENGTH); - QByteArray image(cmd.image().c_str(), length); + QByteArray image(cmd.image().c_str(), cmd.image().length()); int id = userInfo->id(); QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set avatar_bmp=:image where id=:id"); @@ -1457,791 +1093,99 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountImage(const Comm if (!sqlInterface->execSqlQuery(query)) return Response::RespInternalError; - userInfo->set_avatar_bmp(cmd.image().c_str(), length); + userInfo->set_avatar_bmp(cmd.image().c_str(), cmd.image().length()); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const Command_AccountPassword &cmd, - ResponseContainer & /* rc */) +Response::ResponseCode ServerSocketInterface::cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer & /* rc */) { if (authState != PasswordRight) return Response::RespFunctionNotAllowed; - if (cmd.old_password().length() > MAX_NAME_LENGTH) - return Response::RespWrongPassword; QString oldPassword = QString::fromStdString(cmd.old_password()); - QString newPassword; - bool newPasswordNeedsHash = false; - if (cmd.has_new_password()) { - if (cmd.new_password().length() > MAX_NAME_LENGTH) - return Response::RespContextError; - newPassword = QString::fromStdString(cmd.new_password()); - newPasswordNeedsHash = true; - if (!isPasswordLongEnough(newPassword.length())) - return Response::RespPasswordTooShort; - } else if (cmd.hashed_new_password().length() > MAX_NAME_LENGTH) { - return Response::RespContextError; - } else { - newPassword = QString::fromStdString(cmd.hashed_new_password()); - } + QString newPassword = QString::fromStdString(cmd.new_password()); + + // TODO make this configurable? + if(newPassword.length() < 6) + return Response::RespPasswordTooShort; QString userName = QString::fromStdString(userInfo->name()); - if (!databaseInterface->changeUserPassword(userName, oldPassword, true, newPassword, newPasswordNeedsHash)) + + bool changeFailed = databaseInterface->changeUserPassword(userName, oldPassword, newPassword); + + if(changeFailed) return Response::RespWrongPassword; - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd, - ResponseContainer &rc) -{ - const QString userName = nameFromStdString(cmd.user_name()); - const QString clientId = nameFromStdString(cmd.clientid()); - - qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password request from user:" << userName; - - if (!servatrice->getEnableForgotPassword()) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", "Server functionality disabled", false); - - return Response::RespFunctionNotAllowed; - } - - if (servatrice->getEnableForgotPasswordChallenge()) { - Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest; - re->set_challenge_email(true); - rc.setResponseExtension(re); - return Response::RespOk; - } - - if (!sqlInterface->userExists(userName)) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", "User does not exist", false); - - return Response::RespFunctionNotAllowed; - } - - return continuePasswordRequest(userName, clientId, rc); -} - -Response::ResponseCode AbstractServerSocketInterface::continuePasswordRequest(const QString &userName, - const QString &clientId, - ResponseContainer &rc, - bool challenged) -{ - if (sqlInterface->doesForgotPasswordExist(userName)) { - - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", "Request already exists", true); - - Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest; - re->set_challenge_email(false); - rc.setResponseExtension(re); - return Response::RespOk; - } - - QString banReason; - int banTimeRemaining; - if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banTimeRemaining)) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", "User is banned", false); - - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->addForgotPassword(userName)) { - if (servatrice->getEnableForgotPasswordAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", "Failed to create password reset", false); - } - return Response::RespFunctionNotAllowed; - } - - if (servatrice->getEnableForgotPasswordAudit()) { - QString details = - challenged ? "Request does not exist, challenge passed" : "Request does not exist, challenge not requested"; - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_REQUEST", details, true); - } - - Response_ForgotPasswordRequest *re = new Response_ForgotPasswordRequest; - re->set_challenge_email(false); - rc.setResponseExtension(re); - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(const Command_ForgotPasswordReset &cmd, - ResponseContainer &rc) -{ - Q_UNUSED(rc); - QString userName = nameFromStdString(cmd.user_name()); - QString clientId = nameFromStdString(cmd.clientid()); - qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password reset from user:" << userName; - - if (!sqlInterface->doesForgotPasswordExist(userName)) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET", "Request does not exist for user", false); - - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->validateTableColumnStringData("{prefix}_users", "token", userName, - nameFromStdString(cmd.token()))) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), userName.simplified(), - "PASSWORD_RESET", "Failed token validation", false); - - return Response::RespFunctionNotAllowed; - } - - QString password; - bool passwordNeedsHash = false; - if (cmd.has_new_password()) { - if (cmd.new_password().length() > MAX_NAME_LENGTH) - return Response::RespContextError; - password = QString::fromStdString(cmd.new_password()); - passwordNeedsHash = true; - } else if (cmd.hashed_new_password().length() > MAX_NAME_LENGTH) { - return Response::RespContextError; - } else { - password = QString::fromStdString(cmd.hashed_new_password()); - } - - if (sqlInterface->changeUserPassword(nameFromStdString(cmd.user_name()), password, passwordNeedsHash)) { - if (servatrice->getEnableForgotPasswordAudit()) - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET", "", true); - - sqlInterface->removeForgotPassword(nameFromStdString(cmd.user_name())); - return Response::RespOk; - } - - return Response::RespFunctionNotAllowed; -} - -Response::ResponseCode -AbstractServerSocketInterface::cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &cmd, - ResponseContainer &rc) -{ - const QString userName = nameFromStdString(cmd.user_name()); - const QString clientId = nameFromStdString(cmd.clientid()); - - qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password challenge from user:" << userName; - - if (!servatrice->getEnableForgotPasswordChallenge()) { - if (servatrice->getEnableForgotPasswordAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_CHALLENGE", "Feature not enabled", false); - } - return Response::RespFunctionNotAllowed; - } - - if (!sqlInterface->userExists(userName)) { - if (servatrice->getEnableForgotPasswordAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_CHALLENGE", "User does not exist", false); - } - return Response::RespFunctionNotAllowed; - } - - const auto parsedEmailAddress = EmailParser::getParsedEmailAddress(nameFromStdString(cmd.email())); - if (!sqlInterface->validateTableColumnStringData("{prefix}_users", "email", userName, parsedEmailAddress)) { - if (servatrice->getEnableForgotPasswordAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_CHALLENGE", "Failed to answer email challenge question", - false); - } - return Response::RespFunctionNotAllowed; - } - - if (servatrice->getEnableForgotPasswordAudit()) { - sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(), - "PASSWORD_RESET_CHALLENGE", "", true); - } - return continuePasswordRequest(userName, clientId, rc, true); -} - -Response::ResponseCode AbstractServerSocketInterface::cmdRequestPasswordSalt(const Command_RequestPasswordSalt &cmd, - ResponseContainer &rc) -{ - const QString userName = nameFromStdString(cmd.user_name()); - QString passwordSalt = sqlInterface->getUserSalt(userName); - if (passwordSalt.isEmpty()) { - if (server->getRegOnlyServerEnabled()) { - return Response::RespRegistrationRequired; - } else { - // user does not exist but is allowed to log in unregistered without password - return Response::RespOk; - } - } - auto *re = new Response_PasswordSalt; - re->set_password_salt(passwordSalt.toStdString()); - rc.setResponseExtension(re); + return Response::RespOk; } // ADMIN FUNCTIONS. // Permission is checked by the calling function. -Response::ResponseCode -AbstractServerSocketInterface::cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/) { QMetaObject::invokeMethod(server, "updateLoginMessage"); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdShutdownServer(const Command_ShutdownServer &cmd, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer & /*rc*/) { - QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, textFromStdString(cmd.reason())), - Q_ARG(int, cmd.minutes())); + QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, QString::fromStdString(cmd.reason())), Q_ARG(int, cmd.minutes())); return Response::RespOk; } -Response::ResponseCode AbstractServerSocketInterface::cmdReloadConfig(const Command_ReloadConfig & /* cmd */, - ResponseContainer & /*rc*/) +Response::ResponseCode ServerSocketInterface::cmdReloadConfig(const Command_ReloadConfig & /* cmd */, ResponseContainer & /*rc*/) { logDebugMessage("Received admin command: reloading configuration"); settingsCache->sync(); - QMetaObject::invokeMethod(server, "setRequiredFeatures", Q_ARG(QString, server->getRequiredFeatures())); return Response::RespOk; } -bool AbstractServerSocketInterface::addAdminFlagToUser(const QString &userName, int flag) -{ - QSqlQuery *query = - sqlInterface->prepareQuery("update {prefix}_users set admin = (admin | :adminlevel) where name = :username"); - query->bindValue(":adminlevel", flag); - query->bindValue(":username", userName); - if (!sqlInterface->execSqlQuery(query)) { - logger->logMessage(QString("Failed to promote user %1: %2").arg(userName).arg(query->lastError().text())); - return false; - } +Response::ResponseCode ServerSocketInterface::cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/) { - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); - if (user) { - Event_NotifyUser event; - event.set_type(Event_NotifyUser::PROMOTED); - SessionEvent *se = user->prepareSessionEvent(event); - user->sendProtocolItem(*se); - delete se; - } + QString userName = QString::fromStdString(cmd.user_name()); - return true; -} - -bool AbstractServerSocketInterface::removeAdminFlagFromUser(const QString &userName, int flag) -{ - QSqlQuery *query = - sqlInterface->prepareQuery("update {prefix}_users set admin = (admin & ~ :adminlevel) where name = :username"); - query->bindValue(":adminlevel", flag); - query->bindValue(":username", userName); - if (!sqlInterface->execSqlQuery(query)) { - logger->logMessage(QString("Failed to demote user %1: %2").arg(userName).arg(query->lastError().text())); - return false; - } - - AbstractServerSocketInterface *user = - static_cast(server->getUsers().value(userName)); - if (user) { - Event_ConnectionClosed event; - event.set_reason(Event_ConnectionClosed::DEMOTED); - event.set_reason_str("Your moderator and/or judge status has been revoked."); - event.set_end_time(QDateTime::currentDateTime().toSecsSinceEpoch()); - - SessionEvent *se = user->prepareSessionEvent(event); - user->sendProtocolItem(*se); - delete se; - } - - QMetaObject::invokeMethod(user, "prepareDestroy", Qt::QueuedConnection); - return true; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdAdjustMod(const Command_AdjustMod &cmd, - ResponseContainer & /*rc*/) -{ - - QString userName = nameFromStdString(cmd.user_name()); - - if (cmd.has_should_be_mod()) { - if (cmd.should_be_mod()) { - if (!addAdminFlagToUser(userName, 2)) { - return Response::RespInternalError; - } - } else { - if (!removeAdminFlagFromUser(userName, 2)) { - return Response::RespInternalError; - } - } - } - - if (cmd.has_should_be_judge()) { - if (cmd.should_be_judge()) { - if (!addAdminFlagToUser(userName, 4)) { - return Response::RespInternalError; - } - } else { - if (!removeAdminFlagFromUser(userName, 4)) { - return Response::RespInternalError; - } - } - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdGrantReplayAccess(const Command_GrantReplayAccess &cmd, - ResponseContainer & /*rc*/) -{ - // Determine if the replay actually exists already - auto *replayExistsQuery = - sqlInterface->prepareQuery("select count(*) from {prefix}_replays_access where id_game = :idgame"); - replayExistsQuery->bindValue(":idgame", cmd.replay_id()); - if (!sqlInterface->execSqlQuery(replayExistsQuery)) { - return Response::RespInternalError; - } - if (!replayExistsQuery->next()) { - return Response::RespInternalError; - } - - const auto &replayExists = replayExistsQuery->value(0).toInt() > 0; - if (!replayExists) { - return Response::RespContextError; - } - - // Determine the Moderator's User ID (As it's not apart of client, only username is) - auto *getModeratorUserIdQuery = sqlInterface->prepareQuery("select id from {prefix}_users WHERE name = :name"); - getModeratorUserIdQuery->bindValue(":name", QString::fromStdString(cmd.moderator_name())); - if (!sqlInterface->execSqlQuery(getModeratorUserIdQuery)) { - return Response::RespInternalError; - } - if (!getModeratorUserIdQuery->next()) { - return Response::RespInternalError; - } - - const auto &moderator_id = getModeratorUserIdQuery->value(0).toString(); - - // Grant the Moderator access to the replay - auto *grantReplayAccessQuery = - sqlInterface->prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name, do_not_hide) " - "values(:idgame, :idplayer, :replayname, 0)"); - grantReplayAccessQuery->bindValue(":idgame", cmd.replay_id()); - grantReplayAccessQuery->bindValue(":idplayer", moderator_id); - grantReplayAccessQuery->bindValue(":replayname", "Moderator Access Replay Grant"); - - if (!sqlInterface->execSqlQuery(grantReplayAccessQuery)) { - return Response::RespInternalError; - } - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdForceActivateUser(const Command_ForceActivateUser &cmd, - ResponseContainer &rc) -{ - // Determine if user exists - auto *getUserTokenQuery = sqlInterface->prepareQuery("select token from {prefix}_users WHERE name = :name"); - getUserTokenQuery->bindValue(":name", QString::fromStdString(cmd.username_to_activate())); - if (!sqlInterface->execSqlQuery(getUserTokenQuery)) { - // Internal server error - return Response::RespInternalError; - } - if (!getUserTokenQuery->next()) { - // User doesn't exist - return Response::RespNameNotFound; - } - const auto &token = getUserTokenQuery->value(0).toString(); - - // Add audit log that Moderator activated account on behalf of user - const auto &msg = QString("Attempt Force Activation by %1").arg(QString::fromStdString(cmd.moderator_name())); - sqlInterface->addAuditRecord(QString::fromStdString(cmd.username_to_activate()), this->getAddress(), "UNKNOWN", - "ACTIVATE_ACCOUNT", msg, true); - - // Build up activation request - Command_Activate cmdActivate; - cmdActivate.set_user_name(cmd.username_to_activate()); - cmdActivate.set_token(token.toStdString()); - - // Send activation request -- Either User exists or User activated - return cmdActivateAccount(cmdActivate, rc); -} - -Response::ResponseCode AbstractServerSocketInterface::cmdGetAdminNotes(const Command_GetAdminNotes &cmd, - ResponseContainer &rc) -{ - auto *getAdminNotesQuery = sqlInterface->prepareQuery("select adminnotes from {prefix}_users WHERE name = :name"); - getAdminNotesQuery->bindValue(":name", QString::fromStdString(cmd.user_name())); - if (!sqlInterface->execSqlQuery(getAdminNotesQuery)) { - // Internal server error - return Response::RespInternalError; - } - if (!getAdminNotesQuery->next()) { - // User doesn't exist - return Response::RespNameNotFound; - } - const auto &adminNotes = getAdminNotesQuery->value(0).toString(); - - Response_GetAdminNotes *re = new Response_GetAdminNotes; - re->set_user_name(cmd.user_name()); - re->set_notes(adminNotes.toStdString()); - rc.setResponseExtension(re); - - return Response::RespOk; -} - -Response::ResponseCode AbstractServerSocketInterface::cmdUpdateAdminNotes(const Command_UpdateAdminNotes &cmd, - ResponseContainer & /*rc*/) -{ - auto *updateAdminNotesQuery = - sqlInterface->prepareQuery("update {prefix}_users set adminnotes = :adminnotes where name = :name"); - updateAdminNotesQuery->bindValue(":adminnotes", QString::fromStdString(cmd.notes())); - updateAdminNotesQuery->bindValue(":name", QString::fromStdString(cmd.user_name())); - - if (!sqlInterface->execSqlQuery(updateAdminNotesQuery)) { - return Response::RespInternalError; - } - - if (updateAdminNotesQuery->numRowsAffected() == 0) { - return Response::RespNameNotFound; - } - - return Response::RespOk; -} - -TcpServerSocketInterface::TcpServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent) - : AbstractServerSocketInterface(_server, _databaseInterface, parent), messageInProgress(false), - handshakeStarted(false) -{ - socket = new QTcpSocket(this); - socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); - connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); - connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected())); - connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, - SLOT(catchSocketError(QAbstractSocket::SocketError))); -} - -TcpServerSocketInterface::~TcpServerSocketInterface() -{ - logger->logMessage("TcpServerSocketInterface destructor", this); - - flushOutputQueue(); -} - -void TcpServerSocketInterface::initConnection(int socketDescriptor) -{ - // Add this object to the server's list of connections before it can receive socket events. - // Otherwise, in case a of a socket error, it could be removed from the list before it is added. - server->addClient(this); - - socket->setSocketDescriptor(socketDescriptor); - logger->logMessage(QString("Incoming connection: %1").arg(socket->peerAddress().toString()), this); - initSessionDeprecated(); -} - -void TcpServerSocketInterface::initSessionDeprecated() -{ - // dirty hack to make v13 client display the correct error message - - QByteArray buf; - buf.append(""); - writeToSocket(buf); - flushSocket(); -} - -void TcpServerSocketInterface::flushOutputQueue() -{ - QMutexLocker locker(&outputQueueMutex); - if (outputQueue.isEmpty()) - return; - - int totalBytes = 0; - while (!outputQueue.isEmpty()) { - ServerMessage item = outputQueue.takeFirst(); - locker.unlock(); - - QByteArray buf; -#if GOOGLE_PROTOBUF_VERSION > 3001000 - unsigned int size = static_cast(item.ByteSizeLong()); -#else - unsigned int size = static_cast(item.ByteSize()); -#endif - buf.resize(size + 4); - if (item.SerializeToArray(buf.data() + 4, size)) { - buf.data()[3] = (unsigned char)size; - buf.data()[2] = (unsigned char)(size >> 8); - buf.data()[1] = (unsigned char)(size >> 16); - buf.data()[0] = (unsigned char)(size >> 24); - // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. - writeToSocket(buf); - } else { - qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!"; + if (cmd.should_be_mod()) { + QSqlQuery *query = sqlInterface->prepareQuery( + "update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 2); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)){ + logger->logMessage(QString::fromStdString("Failed to promote user %1: %2").arg(userName).arg(query->lastError().text())); + return Response::RespInternalError; } - totalBytes += size + 4; - locker.relock(); - } - locker.unlock(); - emit incTxBytes(totalBytes); - // see above wrt mutex - flushSocket(); -} - -void TcpServerSocketInterface::readClient() -{ - QByteArray data = socket->readAll(); - servatrice->incRxBytes(data.size()); - inputBuffer.append(data); - - do { - if (!messageInProgress) { - if (inputBuffer.size() >= 4) { - messageLength = (((quint32)(unsigned char)inputBuffer[0]) << 24) + - (((quint32)(unsigned char)inputBuffer[1]) << 16) + - (((quint32)(unsigned char)inputBuffer[2]) << 8) + - ((quint32)(unsigned char)inputBuffer[3]); - inputBuffer.remove(0, 4); - messageInProgress = true; - } else - return; - } - if (inputBuffer.size() < messageLength || messageLength < 0) - return; - - CommandContainer newCommandContainer; - bool ok; - try { - ok = newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength); - } catch (std::exception &e) { - qCWarning(TcpServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ << -#ifdef _MSC_VER // Visual Studio - __FUNCTION__ -#else - __PRETTY_FUNCTION__ -#endif - << Qt::endl - << "Exception:" << e.what() << Qt::endl - << "Message coming from:" << getAddress() << Qt::endl - << "Message length:" << messageLength << Qt::endl - << "Message content:" << inputBuffer.toHex(); - } catch (...) { - qCWarning(TcpServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ << -#ifdef _MSC_VER // Visual Studio - __FUNCTION__ -#else - __PRETTY_FUNCTION__ -#endif - << Qt::endl - << "Message coming from:" << getAddress(); - } - inputBuffer.remove(0, messageLength); - messageInProgress = false; - - if (ok) { - // dirty hack to make v13 client display the correct error message - if (handshakeStarted) - processCommandContainer(newCommandContainer); - else if (!newCommandContainer.has_cmd_id()) { - handshakeStarted = true; - if (!initTcpSession()) - prepareDestroy(); - } - // end of hack - } else { - qCWarning(TcpServerSocketInterfaceLog) << "parsing error!"; - } - - } while (!inputBuffer.isEmpty()); -} - -bool TcpServerSocketInterface::initTcpSession() -{ - if (!initSession()) - return false; - - // limit the number of websocket users based on configuration settings - bool enforceUserLimit = settingsCache->value("security/enable_max_user_limit", false).toBool(); - if (enforceUserLimit) { - int userLimit = settingsCache->value("security/max_users_tcp", 500).toInt(); - int playerCount = (server->getTCPUserCount() + 1); - if (playerCount > userLimit) { - std::cerr << "Max Tcp Users Limit Reached, please increase the max_users_tcp setting." << std::endl; - logger->logMessage(QString("Max Tcp Users Limit Reached, please increase the max_users_tcp setting."), - this); - Event_ConnectionClosed event; - event.set_reason(Event_ConnectionClosed::USER_LIMIT_REACHED); - SessionEvent *se = prepareSessionEvent(event); - sendProtocolItem(*se); + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { + Event_NotifyUser event; + event.set_type(Event_NotifyUser::PROMOTED); + SessionEvent *se = user->prepareSessionEvent(event); + user->sendProtocolItem(*se); delete se; - return false; } - } - - return true; -} - -WebsocketServerSocketInterface::WebsocketServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent) - : AbstractServerSocketInterface(_server, _databaseInterface, parent), socket(nullptr) -{ -} - -WebsocketServerSocketInterface::~WebsocketServerSocketInterface() -{ - logger->logMessage("WebsocketServerSocketInterface destructor", this); - - flushOutputQueue(); -} - -void WebsocketServerSocketInterface::initConnection(void *_socket) -{ - if (_socket == nullptr) { - return; - } - socket = (QWebSocket *)_socket; - socket->setParent(this); - // https://bugreports.qt.io/browse/QTBUG-70693 - socket->setMaxAllowedIncomingMessageSize(1500000); // 1.5MB - - address = socket->peerAddress(); - - QByteArray websocketIPHeader = settingsCache->value("server/web_socket_ip_header", "").toByteArray(); - if (websocketIPHeader.length() > 0 && socket->request().hasRawHeader(websocketIPHeader)) { - QString header(socket->request().rawHeader(websocketIPHeader)); - QHostAddress parsed(header); - if (!parsed.isNull()) { - address = parsed; - } - } - - connect(socket, SIGNAL(binaryMessageReceived(const QByteArray &)), this, - SLOT(binaryMessageReceived(const QByteArray &))); - connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(catchSocketError(QAbstractSocket::SocketError))); - connect(socket, SIGNAL(disconnected()), this, SLOT(catchSocketDisconnected())); - - // Add this object to the server's list of connections before it can receive socket events. - // Otherwise, in case of a socket error, it could be removed from the list before it is added. - server->addClient(this); - - logger->logMessage( - QString("Incoming websocket connection: %1 (%2)").arg(address.toString()).arg(socket->peerAddress().toString()), - this); - - if (!initWebsocketSession()) - prepareDestroy(); -} - -bool WebsocketServerSocketInterface::initWebsocketSession() -{ - if (!initSession()) - return false; - - // limit the number of websocket users based on configuration settings - bool enforceUserLimit = settingsCache->value("security/enable_max_user_limit", false).toBool(); - if (enforceUserLimit) { - int userLimit = settingsCache->value("security/max_users_websocket", 500).toInt(); - int playerCount = (server->getWebSocketUserCount() + 1); - if (playerCount > userLimit) { - std::cerr << "Max Websocket Users Limit Reached, please increase the max_users_websocket setting." - << std::endl; - logger->logMessage( - QString("Max Websocket Users Limit Reached, please increase the max_users_websocket setting."), this); - Event_ConnectionClosed event; - event.set_reason(Event_ConnectionClosed::USER_LIMIT_REACHED); - SessionEvent *se = prepareSessionEvent(event); - sendProtocolItem(*se); - delete se; - return false; - } - } - - return true; -} - -void WebsocketServerSocketInterface::flushOutputQueue() -{ - QMutexLocker locker(&outputQueueMutex); - if (outputQueue.isEmpty()) - return; - - qint64 totalBytes = 0; - while (!outputQueue.isEmpty()) { - ServerMessage item = outputQueue.takeFirst(); - locker.unlock(); - - QByteArray buf; -#if GOOGLE_PROTOBUF_VERSION > 3001000 - unsigned int size = static_cast(item.ByteSizeLong()); -#else - unsigned int size = static_cast(item.ByteSize()); -#endif - buf.resize(size); - if (item.SerializeToArray(buf.data(), size)) { - // In case socket->write() calls catchSocketError(), the mutex must not be locked during this call. - writeToSocket(buf); - } else { - qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!"; - } - - totalBytes += size; - locker.relock(); - } - locker.unlock(); - emit incTxBytes(totalBytes); - // see above wrt mutex - flushSocket(); -} - -void WebsocketServerSocketInterface::binaryMessageReceived(const QByteArray &message) -{ - servatrice->incRxBytes(message.size()); - - CommandContainer newCommandContainer; - bool ok; - try { - ok = newCommandContainer.ParseFromArray(message.data(), message.size()); - } catch (std::exception &e) { - qCWarning(WebsocketServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ << -#ifdef _MSC_VER // Visual Studio - __FUNCTION__ -#else - __PRETTY_FUNCTION__ -#endif - << Qt::endl - << "Exception:" << e.what() << Qt::endl - << "Message coming from:" << getAddress() << Qt::endl - << "Message length:" << message.size() << Qt::endl - << "Message content:" << message.toHex(); - } catch (...) { - qCWarning(WebsocketServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ << -#ifdef _MSC_VER // Visual Studio - __FUNCTION__ -#else - __PRETTY_FUNCTION__ -#endif - << Qt::endl - << "Message coming from:" << getAddress(); - } - - if (ok) { - processCommandContainer(newCommandContainer); } else { - qCWarning(WebsocketServerSocketInterfaceLog) << "parsing error!"; - } -} + QSqlQuery *query = sqlInterface->prepareQuery("update {prefix}_users set admin = :adminlevel where name = :username"); + query->bindValue(":adminlevel", 0); + query->bindValue(":username", userName); + if (!sqlInterface->execSqlQuery(query)){ + logger->logMessage(QString::fromStdString("Failed to demote user %1: %2").arg(userName).arg(query->lastError().text())); + return Response::RespInternalError; + } -bool AbstractServerSocketInterface::isPasswordLongEnough(const int passwordLength) -{ - return passwordLength >= servatrice->getMinPasswordLength(); -} + ServerSocketInterface *user = static_cast(server->getUsers().value(userName)); + if (user) { + Event_ConnectionClosed event; + event.set_reason(Event_ConnectionClosed::DEMOTED); + event.set_reason_str("Your moderator status has been revoked."); + event.set_end_time(QDateTime::currentDateTime().toTime_t()); + + SessionEvent *se = user->prepareSessionEvent(event); + user->sendProtocolItem(*se); + delete se; + } + + QMetaObject::invokeMethod(user, "prepareDestroy", Qt::QueuedConnection); + } + + return Response::RespOk; +} \ No newline at end of file diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index e10aa0dde..053749878 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -20,12 +20,12 @@ #ifndef SERVERSOCKETINTERFACE_H #define SERVERSOCKETINTERFACE_H +#include #include #include -#include -#include -#include +#include "server_protocolhandler.h" +class QTcpSocket; class Servatrice; class Servatrice_DatabaseInterface; class DeckList; @@ -43,8 +43,6 @@ class Command_ReplayList; class Command_ReplayDownload; class Command_ReplayModifyMatch; class Command_ReplayDeleteMatch; -class Command_ReplayGetCode; -class Command_ReplaySubmitCode; class Command_BanFromServer; class Command_UpdateServerMessage; @@ -55,196 +53,80 @@ class Command_AccountEdit; class Command_AccountImage; class Command_AccountPassword; -class AbstractServerSocketInterface : public Server_ProtocolHandler + +class ServerSocketInterface : public Server_ProtocolHandler { - Q_OBJECT -protected slots: - void catchSocketError(QAbstractSocket::SocketError socketError); - void catchSocketDisconnected(); - virtual void flushOutputQueue() = 0; + Q_OBJECT +private slots: + void readClient(); + void catchSocketError(QAbstractSocket::SocketError socketError); + void flushOutputQueue(); signals: - void outputQueueChanged(); - void incTxBytes(qint64 amount); - + void outputQueueChanged(); protected: - void logDebugMessage(const QString &message); - bool tooManyRegistrationAttempts(const QString &ipAddress); - - virtual void writeToSocket(QByteArray &data) = 0; - virtual void flushSocket() = 0; - - Servatrice *servatrice; - QList outputQueue; - QMutex outputQueueMutex; - + void logDebugMessage(const QString &message); + bool tooManyRegistrationAttempts(const QString &ipAddress); private: - Servatrice_DatabaseInterface *sqlInterface; - - Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc); - int getDeckPathId(int basePathId, QStringList path); - int getDeckPathId(const QString &path); - bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder); - Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc); - void deckDelDirHelper(int basePathId); - void sendServerMessage(const QString userName, const QString message); - Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc); - Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); - DeckList *getDeckFromDatabase(int deckId); - Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc); - QString createHashForReplay(int gameId); - Response::ResponseCode cmdReplayGetCode(const Command_ReplayGetCode &cmd, ResponseContainer &rc); - Response::ResponseCode cmdReplaySubmitCode(const Command_ReplaySubmitCode &cmd, ResponseContainer &rc); - Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); - Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); - Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); - Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); + QMutex outputQueueMutex; + Servatrice *servatrice; + Servatrice_DatabaseInterface *sqlInterface; + QTcpSocket *socket; + + QByteArray inputBuffer; + QList outputQueue; + bool messageInProgress; + bool handshakeStarted; + int messageLength; + + Response::ResponseCode cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc); + int getDeckPathId(int basePathId, QStringList path); + int getDeckPathId(const QString &path); + bool deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder); + Response::ResponseCode cmdDeckList(const Command_DeckList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckNewDir(const Command_DeckNewDir &cmd, ResponseContainer &rc); + void deckDelDirHelper(int basePathId); + void sendServerMessage(const QString userName, const QString message); + Response::ResponseCode cmdDeckDelDir(const Command_DeckDelDir &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc); + Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); + DeckList *getDeckFromDatabase(int deckId); + Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayDeleteMatch(const Command_ReplayDeleteMatch &cmd, ResponseContainer &rc); + Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); + Response::ResponseCode cmdWarnUser(const Command_WarnUser &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetLogHistory(const Command_ViewLogHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetBanHistory(const Command_GetBanHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetWarnList(const Command_GetWarnList &cmd, ResponseContainer &rc); + Response::ResponseCode cmdGetWarnHistory(const Command_GetWarnHistory &cmd, ResponseContainer &rc); + Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); + Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); - Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig & /* cmd */, ResponseContainer & /*rc*/); - Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); - Response::ResponseCode cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd, ResponseContainer &rc); - Response::ResponseCode continuePasswordRequest(const QString &userName, - const QString &clientId, - ResponseContainer &rc, - bool challenged = false); - Response::ResponseCode cmdForgotPasswordReset(const Command_ForgotPasswordReset &cmd, ResponseContainer &rc); - Response::ResponseCode cmdForgotPasswordChallenge(const Command_ForgotPasswordChallenge &cmd, - ResponseContainer &rc); - Response::ResponseCode cmdRequestPasswordSalt(const Command_RequestPasswordSalt &cmd, ResponseContainer &rc); - Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); - Response::ResponseCode - processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); - Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); - - Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); - Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); - Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); - Response::ResponseCode cmdGrantReplayAccess(const Command_GrantReplayAccess &cmd, ResponseContainer &rc); - Response::ResponseCode cmdForceActivateUser(const Command_ForceActivateUser &cmd, ResponseContainer &rc); - - Response::ResponseCode cmdGetAdminNotes(const Command_GetAdminNotes &cmd, ResponseContainer &rc); - Response::ResponseCode cmdUpdateAdminNotes(const Command_UpdateAdminNotes &cmd, ResponseContainer &rc); - - bool addAdminFlagToUser(const QString &user, int flag); - bool removeAdminFlagFromUser(const QString &user, int flag); - - bool isPasswordLongEnough(const int passwordLength); - void removeSaidMessages(const QString &userName, int amount); + Response::ResponseCode cmdReloadConfig(const Command_ReloadConfig &/* cmd */, ResponseContainer & /*rc*/); + Response::ResponseCode cmdAdjustMod(const Command_AdjustMod &cmd, ResponseContainer & /*rc*/); + + Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); + Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); + Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountEdit(const Command_AccountEdit &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); + Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); public: - AbstractServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent = 0); - ~AbstractServerSocketInterface() - { - } - bool initSession(); + ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); + ~ServerSocketInterface(); + void initSessionDeprecated(); + bool initSession(); + QHostAddress getPeerAddress() const { return socket->peerAddress(); } + QString getAddress() const { return socket->peerAddress().toString(); } - virtual QHostAddress getPeerAddress() const = 0; - virtual QString getAddress() const = 0; - - void transmitProtocolItem(const ServerMessage &item); -}; - -class TcpServerSocketInterface : public AbstractServerSocketInterface -{ - Q_OBJECT -public: - TcpServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent = 0); - ~TcpServerSocketInterface(); - - QHostAddress getPeerAddress() const - { - return socket->peerAddress(); - } - QString getAddress() const - { - return socket->peerAddress().toString(); - } - QString getConnectionType() const - { - return "tcp"; - } - -private: - QTcpSocket *socket; - QByteArray inputBuffer; - bool messageInProgress; - bool handshakeStarted; - int messageLength; - -protected: - void writeToSocket(QByteArray &data) - { - socket->write(data); - } - void flushSocket() - { - socket->flush(); - } - void initSessionDeprecated(); - bool initTcpSession(); -protected slots: - void readClient(); - void flushOutputQueue(); + void transmitProtocolItem(const ServerMessage &item); public slots: - void initConnection(int socketDescriptor); -}; - -class WebsocketServerSocketInterface : public AbstractServerSocketInterface -{ - Q_OBJECT -public: - WebsocketServerSocketInterface(Servatrice *_server, - Servatrice_DatabaseInterface *_databaseInterface, - QObject *parent = nullptr); - ~WebsocketServerSocketInterface(); - - QHostAddress getPeerAddress() const - { - return address; - } - QString getAddress() const - { - return address.toString(); - } - QString getConnectionType() const - { - return "websocket"; - } - -private: - QWebSocket *socket; - QHostAddress address; - -protected: - void writeToSocket(QByteArray &data) - { - socket->sendBinaryMessage(data); - } - void flushSocket() - { - socket->flush(); - } - bool initWebsocketSession(); -protected slots: - void binaryMessageReceived(const QByteArray &message); - void flushOutputQueue(); -public slots: - void initConnection(void *_socket); + void initConnection(int socketDescriptor); }; #endif diff --git a/servatrice/src/settingscache.cpp b/servatrice/src/settingscache.cpp index f6dcd5fc8..1adad6291 100644 --- a/servatrice/src/settingscache.cpp +++ b/servatrice/src/settingscache.cpp @@ -1,45 +1,45 @@ #include "settingscache.h" - #include -#include #include -#include +#if QT_VERSION >= 0x050000 + #include +#else + #include +#endif -SettingsCache::SettingsCache(const QString &fileName, QSettings::Format format, QObject *parent) - : QSettings(fileName, format, parent) +SettingsCache::SettingsCache(const QString & fileName, QSettings::Format format, QObject * parent) +:QSettings(fileName, format, parent) { - // first, figure out if we are running in portable mode - isPortableBuild = QFile::exists(qApp->applicationDirPath() + "/portable.dat"); - QStringList disallowedRegExpStr = value("users/disallowedregexp", "").toString().split(",", Qt::SkipEmptyParts); - disallowedRegExpStr.removeDuplicates(); - for (const QString ®ExpStr : disallowedRegExpStr) { - disallowedRegExp.append(QRegularExpression(QString("\\A%1\\z").arg(regExpStr))); - } } -QString SettingsCache::guessConfigurationPath() +QString SettingsCache::guessConfigurationPath(QString & specificPath) { - const QString fileName = "servatrice.ini"; - if (QFile::exists(qApp->applicationDirPath() + "/portable.dat")) { - qDebug() << "Portable mode enabled"; - return fileName; - } - + const QString fileName="servatrice.ini"; + #ifdef PORTABLE_BUILD + return fileName; + #endif QString guessFileName; + // specific path + if(!specificPath.isEmpty() && QFile::exists(specificPath)) + return specificPath; // application directory path guessFileName = QCoreApplication::applicationDirPath() + "/" + fileName; - if (QFile::exists(guessFileName)) + if(QFile::exists(guessFileName)) return guessFileName; #ifdef Q_OS_UNIX // /etc guessFileName = "/etc/servatrice/" + fileName; - if (QFile::exists(guessFileName)) + if(QFile::exists(guessFileName)) return guessFileName; #endif - guessFileName = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/" + fileName; +#if QT_VERSION >= 0x050000 + guessFileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + fileName; +#else + guessFileName = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/" + fileName; +#endif return guessFileName; } diff --git a/servatrice/src/settingscache.h b/servatrice/src/settingscache.h index 6922d3508..7284d273c 100644 --- a/servatrice/src/settingscache.h +++ b/servatrice/src/settingscache.h @@ -1,27 +1,16 @@ #ifndef SERVATRICE_SETTINGSCACHE_H #define SERVATRICE_SETTINGSCACHE_H -#include -#include #include #include -class SettingsCache : public QSettings -{ +class SettingsCache : public QSettings { Q_OBJECT private: - bool isPortableBuild; - + QSettings *settings; public: - SettingsCache(const QString &fileName = "servatrice.ini", - QSettings::Format format = QSettings::IniFormat, - QObject *parent = 0); - static QString guessConfigurationPath(); - QList disallowedRegExp; - bool getIsPortableBuild() const - { - return isPortableBuild; - } + SettingsCache(const QString & fileName="servatrice.ini", QSettings::Format format=QSettings::IniFormat, QObject * parent = 0); + static QString guessConfigurationPath(QString & specificPath); }; extern SettingsCache *settingsCache; diff --git a/servatrice/src/signalhandler.cpp b/servatrice/src/signalhandler.cpp index f7cb207a9..d06fc1dbe 100644 --- a/servatrice/src/signalhandler.cpp +++ b/servatrice/src/signalhandler.cpp @@ -1,26 +1,26 @@ -#include "signalhandler.h" - -#include "main.h" -#include "server_logger.h" -#include "settingscache.h" - #include +#include "signalhandler.h" +#include "server_logger.h" +#include "settingscache.h" +#include "main.h" + #ifdef Q_OS_UNIX -#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include +#include #endif #define SIGSEGV_TRACE_LINES 40 int SignalHandler::sigHupFD[2]; -SignalHandler::SignalHandler(QObject *parent) : QObject(parent), snHup(nullptr) +SignalHandler::SignalHandler(QObject *parent) +: QObject(parent) { #ifdef Q_OS_UNIX ::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD); @@ -34,14 +34,14 @@ SignalHandler::SignalHandler(QObject *parent) : QObject(parent), snHup(nullptr) hup.sa_flags = 0; hup.sa_flags |= SA_RESTART; sigaction(SIGHUP, &hup, 0); - + struct sigaction segv; segv.sa_handler = SignalHandler::sigSegvHandler; segv.sa_flags = SA_RESETHAND; sigemptyset(&segv.sa_mask); sigaction(SIGSEGV, &segv, 0); sigaction(SIGABRT, &segv, 0); - + signal(SIGPIPE, SIG_IGN); #endif } @@ -69,13 +69,13 @@ void SignalHandler::internalSigHupHandler() logger->rotateLogs(); settingsCache->sync(); - + snHup->setEnabled(true); } -#ifdef Q_OS_UNIX void SignalHandler::sigSegvHandler(int sig) { +#ifdef Q_OS_UNIX void *array[SIGSEGV_TRACE_LINES]; size_t size; @@ -90,15 +90,12 @@ void SignalHandler::sigSegvHandler(int sig) logger->logMessage("CRASH: SIGSEGV"); else if (sig == SIGABRT) logger->logMessage("CRASH: SIGABRT"); - + logger->deleteLater(); loggerThread->wait(); delete loggerThread; - + raise(sig); -} -#else -void SignalHandler::sigSegvHandler(int /* sig */) -{ -} #endif +} + diff --git a/servatrice/src/signalhandler.h b/servatrice/src/signalhandler.h index bf8d9e52a..314afa3b5 100644 --- a/servatrice/src/signalhandler.h +++ b/servatrice/src/signalhandler.h @@ -5,22 +5,20 @@ class QSocketNotifier; -class SignalHandler : public QObject +class SignalHandler: public QObject { - Q_OBJECT + Q_OBJECT public: - SignalHandler(QObject *parent = 0); - ~SignalHandler() - { - } - static void sigHupHandler(int /* sig */); - static void sigSegvHandler(int sig); - + SignalHandler(QObject *parent = 0); + ~SignalHandler() { }; + static void sigHupHandler(int /* sig */); + static void sigSegvHandler(int sig); private: - static int sigHupFD[2]; - QSocketNotifier *snHup; + static int sigHupFD[2]; + QSocketNotifier *snHup; private slots: - void internalSigHupHandler(); + void internalSigHupHandler(); + }; #endif diff --git a/servatrice/src/smtp/qxthmac.cpp b/servatrice/src/smtp/qxthmac.cpp index 3b7489f72..12c6ae745 100644 --- a/servatrice/src/smtp/qxthmac.cpp +++ b/servatrice/src/smtp/qxthmac.cpp @@ -26,6 +26,9 @@ #include "qxthmac.h" #include +#if QT_VERSION >= 0x040300 + + /* \class QxtHmac @@ -167,11 +170,7 @@ bool QxtHmac::verify(const QByteArray& otherInner) void QxtHmac::addData(const char* data, int length) { Q_ASSERT(qxt_d().opad.size()); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) - qxt_d().ihash->addData(QByteArrayView(data, length)); -#else qxt_d().ihash->addData(data, length); -#endif qxt_d().result.clear(); } @@ -208,3 +207,5 @@ bool QxtHmac::verify(const QByteArray& key, const QByteArray& hmac, const QByteA d->ohash->addData(inner); return hmac == d->ohash->result(); } + +#endif diff --git a/servatrice/src/smtp/qxthmac.h b/servatrice/src/smtp/qxthmac.h index e4d133b0a..8e0d3d4a1 100644 --- a/servatrice/src/smtp/qxthmac.h +++ b/servatrice/src/smtp/qxthmac.h @@ -27,6 +27,11 @@ #define QXTHMAC_H #include + +#if QT_VERSION < 0x040300 +# warning QxtHmac requires Qt 4.3.0 or greater +#else + #include #include "qxtglobal.h" @@ -56,3 +61,4 @@ private: }; #endif +#endif diff --git a/servatrice/src/smtp/qxtmail_p.h b/servatrice/src/smtp/qxtmail_p.h index 48e8607d4..a9a9dc901 100644 --- a/servatrice/src/smtp/qxtmail_p.h +++ b/servatrice/src/smtp/qxtmail_p.h @@ -28,8 +28,7 @@ #include #define QXT_MUST_QP(x) (x < char(32) || x > char(126) || x == '=' || x == '?') -QByteArray qxt_fold_mime_header(const QString &key, - const QString &value, - const QByteArray &prefix = QByteArray()); +QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, + const QByteArray& prefix = QByteArray()); #endif // QXTMAIL_P_H diff --git a/servatrice/src/smtp/qxtmailattachment.cpp b/servatrice/src/smtp/qxtmailattachment.cpp index 8f494c350..59ad464f8 100644 --- a/servatrice/src/smtp/qxtmailattachment.cpp +++ b/servatrice/src/smtp/qxtmailattachment.cpp @@ -34,6 +34,7 @@ #include "qxtmailattachment.h" #include "qxtmail_p.h" +#include #include #include #include @@ -142,9 +143,9 @@ QHash QxtMailAttachment::extraHeaders() const return qxt_d->extraHeaders; } -QByteArray QxtMailAttachment::extraHeader(const QString& key) const +QString QxtMailAttachment::extraHeader(const QString& key) const { - return qxt_d->extraHeaders[key.toLower()].toLatin1(); + return qxt_d->extraHeaders[key.toLower()]; } bool QxtMailAttachment::hasExtraHeader(const QString& key) const @@ -161,7 +162,7 @@ void QxtMailAttachment::setExtraHeaders(const QHash& a) { QHash& headers = qxt_d->extraHeaders; headers.clear(); - for (const QString& key: a.keys()) + foreach(const QString& key, a.keys()) { headers[key.toLower()] = a[key]; } @@ -186,10 +187,11 @@ QByteArray QxtMailAttachment::mimeData() return QByteArray(); } + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); QByteArray rv = "Content-Type: " + qxt_d->contentType.toLatin1() + "\r\nContent-Transfer-Encoding: base64\r\n"; - for(const QString& r: qxt_d->extraHeaders.keys()) + foreach(const QString& r, qxt_d->extraHeaders.keys()) { - rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r)); + rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1); } rv += "\r\n"; diff --git a/servatrice/src/smtp/qxtmailattachment.h b/servatrice/src/smtp/qxtmailattachment.h index 62a0e9ad5..23955ff9e 100644 --- a/servatrice/src/smtp/qxtmailattachment.h +++ b/servatrice/src/smtp/qxtmailattachment.h @@ -27,41 +27,41 @@ #include "qxtglobal.h" -#include +#include #include -#include +#include #include #include -#include +#include struct QxtMailAttachmentPrivate; class QXT_NETWORK_EXPORT QxtMailAttachment { public: QxtMailAttachment(); - QxtMailAttachment(const QxtMailAttachment &other); - QxtMailAttachment(const QByteArray &content, const QString &contentType = QString("application/octet-stream")); - QxtMailAttachment(QIODevice *content, const QString &contentType = QString("application/octet-stream")); - QxtMailAttachment &operator=(const QxtMailAttachment &other); + QxtMailAttachment(const QxtMailAttachment& other); + QxtMailAttachment(const QByteArray& content, const QString& contentType = QString("application/octet-stream")); + QxtMailAttachment(QIODevice* content, const QString& contentType = QString("application/octet-stream")); + QxtMailAttachment& operator=(const QxtMailAttachment& other); ~QxtMailAttachment(); - static QxtMailAttachment fromFile(const QString &filename); + static QxtMailAttachment fromFile(const QString& filename); - QIODevice *content() const; - void setContent(const QByteArray &content); - void setContent(QIODevice *content); + QIODevice* content() const; + void setContent(const QByteArray& content); + void setContent(QIODevice* content); bool deleteContent() const; void setDeleteContent(bool enable); QString contentType() const; - void setContentType(const QString &contentType); + void setContentType(const QString& contentType); QHash extraHeaders() const; - QByteArray extraHeader(const QString &) const; - bool hasExtraHeader(const QString &) const; - void setExtraHeader(const QString &key, const QString &value); - void setExtraHeaders(const QHash &); - void removeExtraHeader(const QString &key); + QString extraHeader(const QString&) const; + bool hasExtraHeader(const QString&) const; + void setExtraHeader(const QString& key, const QString& value); + void setExtraHeaders(const QHash&); + void removeExtraHeader(const QString& key); QByteArray mimeData(); diff --git a/servatrice/src/smtp/qxtmailmessage.cpp b/servatrice/src/smtp/qxtmailmessage.cpp index ff376c1a9..d90740d90 100644 --- a/servatrice/src/smtp/qxtmailmessage.cpp +++ b/servatrice/src/smtp/qxtmailmessage.cpp @@ -23,39 +23,30 @@ ** ****************************************************************************/ + /*! * \class QxtMailMessage * \inmodule QxtNetwork * \brief The QxtMailMessage class encapsulates an e-mail according to RFC 2822 and related specifications + * TODO: {implicitshared} */ -//! \todo {implicitshared} + + #include "qxtmailmessage.h" - #include "qxtmail_p.h" - -#include +#include #include +#include #include -static bool isASCII(const QString &string) { - for(const QChar &chr : string){ - if(chr.unicode() > 0x7f) - return false; - } - return true; -} struct QxtMailMessagePrivate : public QSharedData { - QxtMailMessagePrivate() - { - } - QxtMailMessagePrivate(const QxtMailMessagePrivate &other) - : QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc), - subject(other.subject), body(other.body), sender(other.sender), extraHeaders(other.extraHeaders), - attachments(other.attachments) - { - } + QxtMailMessagePrivate() {} + QxtMailMessagePrivate(const QxtMailMessagePrivate& other) + : QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc), + subject(other.subject), body(other.body), sender(other.sender), + extraHeaders(other.extraHeaders), attachments(other.attachments) {} QStringList rcptTo, rcptCc, rcptBcc; QString subject, body, sender; QHash extraHeaders; @@ -68,12 +59,12 @@ QxtMailMessage::QxtMailMessage() qxt_d = new QxtMailMessagePrivate; } -QxtMailMessage::QxtMailMessage(const QxtMailMessage &other) : qxt_d(other.qxt_d) +QxtMailMessage::QxtMailMessage(const QxtMailMessage& other) : qxt_d(other.qxt_d) { // trivial copy constructor } -QxtMailMessage::QxtMailMessage(const QString &sender, const QString &recipient) +QxtMailMessage::QxtMailMessage(const QString& sender, const QString& recipient) { qxt_d = new QxtMailMessagePrivate; setSender(sender); @@ -85,7 +76,7 @@ QxtMailMessage::~QxtMailMessage() // trivial destructor } -QxtMailMessage &QxtMailMessage::operator=(const QxtMailMessage &other) +QxtMailMessage& QxtMailMessage::operator=(const QxtMailMessage & other) { qxt_d = other.qxt_d; return *this; @@ -96,7 +87,7 @@ QString QxtMailMessage::sender() const return qxt_d->sender; } -void QxtMailMessage::setSender(const QString &a) +void QxtMailMessage::setSender(const QString& a) { qxt_d->sender = a; } @@ -106,7 +97,7 @@ QString QxtMailMessage::subject() const return qxt_d->subject; } -void QxtMailMessage::setSubject(const QString &a) +void QxtMailMessage::setSubject(const QString& a) { qxt_d->subject = a; } @@ -116,7 +107,7 @@ QString QxtMailMessage::body() const return qxt_d->body; } -void QxtMailMessage::setBody(const QString &a) +void QxtMailMessage::setBody(const QString& a) { qxt_d->body = a; } @@ -130,7 +121,7 @@ QStringList QxtMailMessage::recipients(QxtMailMessage::RecipientType type) const return qxt_d->rcptTo; } -void QxtMailMessage::addRecipient(const QString &a, QxtMailMessage::RecipientType type) +void QxtMailMessage::addRecipient(const QString& a, QxtMailMessage::RecipientType type) { if (type == Bcc) qxt_d->rcptBcc.append(a); @@ -140,7 +131,7 @@ void QxtMailMessage::addRecipient(const QString &a, QxtMailMessage::RecipientTyp qxt_d->rcptTo.append(a); } -void QxtMailMessage::removeRecipient(const QString &a) +void QxtMailMessage::removeRecipient(const QString& a) { qxt_d->rcptTo.removeAll(a); qxt_d->rcptCc.removeAll(a); @@ -152,31 +143,32 @@ QHash QxtMailMessage::extraHeaders() const return qxt_d->extraHeaders; } -QByteArray QxtMailMessage::extraHeader(const QString &key) const +QString QxtMailMessage::extraHeader(const QString& key) const { - return qxt_d->extraHeaders[key.toLower()].toLatin1(); + return qxt_d->extraHeaders[key.toLower()]; } -bool QxtMailMessage::hasExtraHeader(const QString &key) const +bool QxtMailMessage::hasExtraHeader(const QString& key) const { return qxt_d->extraHeaders.contains(key.toLower()); } -void QxtMailMessage::setExtraHeader(const QString &key, const QString &value) +void QxtMailMessage::setExtraHeader(const QString& key, const QString& value) { qxt_d->extraHeaders[key.toLower()] = value; } -void QxtMailMessage::setExtraHeaders(const QHash &a) +void QxtMailMessage::setExtraHeaders(const QHash& a) { - QHash &headers = qxt_d->extraHeaders; + QHash& headers = qxt_d->extraHeaders; headers.clear(); - for (const QString &key : a.keys()) { + foreach(const QString& key, a.keys()) + { headers[key.toLower()] = a[key]; } } -void QxtMailMessage::removeExtraHeader(const QString &key) +void QxtMailMessage::removeExtraHeader(const QString& key) { qxt_d->extraHeaders.remove(key.toLower()); } @@ -186,40 +178,46 @@ QHash QxtMailMessage::attachments() const return qxt_d->attachments; } -QxtMailAttachment QxtMailMessage::attachment(const QString &filename) const +QxtMailAttachment QxtMailMessage::attachment(const QString& filename) const { return qxt_d->attachments[filename]; } -void QxtMailMessage::addAttachment(const QString &filename, const QxtMailAttachment &attach) +void QxtMailMessage::addAttachment(const QString& filename, const QxtMailAttachment& attach) { - if (qxt_d->attachments.contains(filename)) { + if (qxt_d->attachments.contains(filename)) + { qWarning() << "QxtMailMessage::addAttachment: " << filename << " already in use"; int i = 1; - while (qxt_d->attachments.contains(filename + "." + QString::number(i))) { + while (qxt_d->attachments.contains(filename + "." + QString::number(i))) + { i++; } - qxt_d->attachments[filename + "." + QString::number(i)] = attach; - } else { + qxt_d->attachments[filename+"."+QString::number(i)] = attach; + } + else + { qxt_d->attachments[filename] = attach; } } -void QxtMailMessage::removeAttachment(const QString &filename) +void QxtMailMessage::removeAttachment(const QString& filename) { qxt_d->attachments.remove(filename); } -QByteArray qxt_fold_mime_header(const QString &key, const QString &value, const QByteArray &prefix) +QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix) { QByteArray rv = ""; QByteArray line = key.toLatin1() + ": "; - if (!prefix.isEmpty()) - line += prefix; - if (!value.contains("=?") && isASCII(value)) { + if (!prefix.isEmpty()) line += prefix; + if (!value.contains("=?") && latin1->canEncode(value)) + { bool firstWord = true; - for (const QByteArray &word : value.toLatin1().split(' ')) { - if (line.size() > 78) { + foreach(const QByteArray& word, value.toLatin1().split(' ')) + { + if (line.size() > 78) + { rv = rv + line + "\r\n"; line.clear(); } @@ -229,7 +227,9 @@ QByteArray qxt_fold_mime_header(const QString &key, const QString &value, const line += " " + word; firstWord = false; } - } else { + } + else + { // The text cannot be losslessly encoded as Latin-1. Therefore, we // must use quoted-printable or base64 encoding. This is a quick // heuristic based on the first 100 characters to see which @@ -237,33 +237,43 @@ QByteArray qxt_fold_mime_header(const QString &key, const QString &value, const QByteArray utf8 = value.toUtf8(); int ct = utf8.length(); int nonAscii = 0; - for (int i = 0; i < ct && i < 100; i++) { - if (QXT_MUST_QP(utf8[i])) - nonAscii++; + for (int i = 0; i < ct && i < 100; i++) + { + if (QXT_MUST_QP(utf8[i])) nonAscii++; } - if (nonAscii > 20) { + if (nonAscii > 20) + { // more than 20%-ish non-ASCII characters: use base64 QByteArray base64 = utf8.toBase64(); ct = base64.length(); line += "=?utf-8?b?"; - for (int i = 0; i < ct; i += 4) { - if (line.length() > 72) { + for (int i = 0; i < ct; i += 4) + { + if (line.length() > 72) + { rv += line + "?\r\n"; line = " =?utf-8?b?"; } line = line + base64.mid(i, 4); } - } else { + } + else + { // otherwise use Q-encoding line += "=?utf-8?q?"; - for (int i = 0; i < ct; i++) { - if (line.length() > 73) { + for (int i = 0; i < ct; i++) + { + if (line.length() > 73) + { rv += line + "?\r\n"; line = " =?utf-8?q?"; } - if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ') { + if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ') + { line += "=" + utf8.mid(i, 1).toHex().toUpper(); - } else { + } + else + { line += utf8[i]; } } @@ -280,75 +290,91 @@ QByteArray QxtMailMessage::rfc2822() const // Use base64 if requested bool useBase64 = (extraHeader("Content-Transfer-Encoding").toLower() == "base64"); // Check to see if plain text is ASCII-clean; assume it isn't if QP or base64 was requested - bool bodyIsAscii = !useQuotedPrintable && !useBase64 && isASCII(body()); + QTextCodec* latin1 = QTextCodec::codecForName("latin1"); + bool bodyIsAscii = latin1->canEncode(body()) && !useQuotedPrintable && !useBase64; QHash attach = attachments(); QByteArray rv; - if (!sender().isEmpty() && !hasExtraHeader("From")) { - rv += qxt_fold_mime_header("From", sender()); + if (!sender().isEmpty() && !hasExtraHeader("From")) + { + rv += qxt_fold_mime_header("From", sender(), latin1); } - if (!qxt_d->rcptTo.isEmpty()) { - rv += qxt_fold_mime_header("To", qxt_d->rcptTo.join(", ")); + if (!qxt_d->rcptTo.isEmpty()) + { + rv += qxt_fold_mime_header("To", qxt_d->rcptTo.join(", "), latin1); } - if (!qxt_d->rcptCc.isEmpty()) { - rv += qxt_fold_mime_header("Cc", qxt_d->rcptCc.join(", ")); + if (!qxt_d->rcptCc.isEmpty()) + { + rv += qxt_fold_mime_header("Cc", qxt_d->rcptCc.join(", "), latin1); } - if (!subject().isEmpty()) { - rv += qxt_fold_mime_header("Subject", subject()); + if (!subject().isEmpty()) + { + rv += qxt_fold_mime_header("Subject", subject(), latin1); } - if (!bodyIsAscii) { + if (!bodyIsAscii) + { if (!hasExtraHeader("MIME-Version") && !attach.count()) rv += "MIME-Version: 1.0\r\n"; // If no transfer encoding has been requested, guess. // Heuristic: If >20% of the first 100 characters aren't // 7-bit clean, use base64, otherwise use Q-P. - if (!bodyIsAscii && !useQuotedPrintable && !useBase64) { + if(!bodyIsAscii && !useQuotedPrintable && !useBase64) + { QString b = body(); int nonAscii = 0; int ct = b.length(); - for (int i = 0; i < ct && i < 100; i++) { - if (QXT_MUST_QP(b[i])) - nonAscii++; + for (int i = 0; i < ct && i < 100; i++) + { + if (QXT_MUST_QP(b[i])) nonAscii++; } useQuotedPrintable = !(nonAscii > 20); useBase64 = !useQuotedPrintable; } } - if (attach.count()) { + if (attach.count()) + { if (qxt_d->boundary.isEmpty()) qxt_d->boundary = QUuid::createUuid().toString().toLatin1().replace("{", "").replace("}", ""); if (!hasExtraHeader("MIME-Version")) rv += "MIME-Version: 1.0\r\n"; if (!hasExtraHeader("Content-Type")) rv += "Content-Type: multipart/mixed; boundary=" + qxt_d->boundary + "\r\n"; - } else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding")) { - if (!useQuotedPrintable) { + } + else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding")) + { + if (!useQuotedPrintable) + { // base64 rv += "Content-Transfer-Encoding: base64\r\n"; - } else { + } + else + { // quoted-printable rv += "Content-Transfer-Encoding: quoted-printable\r\n"; } } - for (const QString &r : qxt_d->extraHeaders.keys()) { - if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count()) { + foreach(const QString& r, qxt_d->extraHeaders.keys()) + { + if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count()) + { // Since we're in multipart mode, we'll be outputting this later continue; } - rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r)); + rv += qxt_fold_mime_header(r.toLatin1(), extraHeader(r), latin1); } rv += "\r\n"; - if (attach.count()) { + if (attach.count()) + { // we're going to have attachments, so output the lead-in for the message body rv += "This is a message with multiple parts in MIME format.\r\n"; rv += "--" + qxt_d->boundary + "\r\nContent-Type: "; @@ -356,13 +382,19 @@ QByteArray QxtMailMessage::rfc2822() const rv += extraHeader("Content-Type") + "\r\n"; else rv += "text/plain; charset=UTF-8\r\n"; - if (hasExtraHeader("Content-Transfer-Encoding")) { + if (hasExtraHeader("Content-Transfer-Encoding")) + { rv += "Content-Transfer-Encoding: " + extraHeader("Content-Transfer-Encoding") + "\r\n"; - } else if (!bodyIsAscii) { - if (!useQuotedPrintable) { + } + else if (!bodyIsAscii) + { + if (!useQuotedPrintable) + { // base64 rv += "Content-Transfer-Encoding: base64\r\n"; - } else { + } + else + { // quoted-printable rv += "Content-Transfer-Encoding: quoted-printable\r\n"; } @@ -370,102 +402,132 @@ QByteArray QxtMailMessage::rfc2822() const rv += "\r\n"; } - if (bodyIsAscii) { - QByteArray b = body().toLatin1(); + if (bodyIsAscii) + { + QByteArray b = latin1->fromUnicode(body()); int len = b.length(); QByteArray line = ""; QByteArray word = ""; - for (int i = 0; i < len; i++) { - if (b[i] == '\n' || b[i] == '\r') { - if (line.isEmpty()) { + for (int i = 0; i < len; i++) + { + if (b[i] == '\n' || b[i] == '\r') + { + if (line.isEmpty()) + { line = word; word = ""; - } else if (line.length() + word.length() + 1 <= 78) { + } + else if (line.length() + word.length() + 1 <= 78) + { line = line + ' ' + word; word = ""; } - if (line.isEmpty()) - continue; - if (line[0] == '.') + if(line[0] == '.') rv += "."; rv += line + "\r\n"; - if ((b[i + 1] == '\n' || b[i + 1] == '\r') && b[i] != b[i + 1]) { + if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1]) + { // If we're looking at a CRLF pair, skip the second half i++; } line = word; - } else if (b[i] == ' ') { - if (line.length() + word.length() + 1 > 78) { - if (line[0] == '.') + } + else if (b[i] == ' ') + { + if (line.length() + word.length() + 1 > 78) + { + if(line[0] == '.') rv += "."; rv += line + "\r\n"; line = word; - } else if (line.isEmpty()) { + } + else if (line.isEmpty()) + { line = word; - } else { + } + else + { line = line + ' ' + word; } word = ""; - } else { + } + else + { word += b[i]; } } - if (line.length() + word.length() + 1 > 78) { - if (line[0] == '.') + if (line.length() + word.length() + 1 > 78) + { + if(line[0] == '.') rv += "."; rv += line + "\r\n"; line = word; - } else if (!word.isEmpty()) { + } + else if (!word.isEmpty()) + { line += ' ' + word; } - if (!line.isEmpty()) { - if (line[0] == '.') + if(!line.isEmpty()) { + if(line[0] == '.') rv += "."; rv += line + "\r\n"; } - } else if (useQuotedPrintable) { + } + else if (useQuotedPrintable) + { QByteArray b = body().toUtf8(); int ct = b.length(); QByteArray line; - for (int i = 0; i < ct; i++) { - if (b[i] == '\n' || b[i] == '\r') { - if (line[0] == '.') + for (int i = 0; i < ct; i++) + { + if(b[i] == '\n' || b[i] == '\r') + { + if(line[0] == '.') rv += "."; rv += line + "\r\n"; line = ""; - if ((b[i + 1] == '\n' || b[i + 1] == '\r') && b[i] != b[i + 1]) { + if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1]) + { // If we're looking at a CRLF pair, skip the second half i++; } - } else if (line.length() > 74) { + } + else if (line.length() > 74) + { rv += line + "=\r\n"; line = ""; } - if (QXT_MUST_QP(b[i])) { + if (QXT_MUST_QP(b[i])) + { line += "=" + b.mid(i, 1).toHex().toUpper(); - } else { + } + else + { line += b[i]; } } - if (!line.isEmpty()) { - if (line[0] == '.') + if(!line.isEmpty()) { + if(line[0] == '.') rv += "."; rv += line + "\r\n"; } - } else /* base64 */ + } + else /* base64 */ { QByteArray b = body().toUtf8().toBase64(); int ct = b.length(); - for (int i = 0; i < ct; i += 78) { + for (int i = 0; i < ct; i += 78) + { rv += b.mid(i, 78) + "\r\n"; } } - if (attach.count()) { - for (const QString &filename : attach.keys()) { + if (attach.count()) + { + foreach(const QString& filename, attach.keys()) + { rv += "--" + qxt_d->boundary + "\r\n"; - rv += - qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), "attachment; filename="); + rv += qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), latin1, "attachment; filename="); rv += attach[filename].mimeData(); } rv += "--" + qxt_d->boundary + "--\r\n"; diff --git a/servatrice/src/smtp/qxtmailmessage.h b/servatrice/src/smtp/qxtmailmessage.h index 584e8fcc5..5fd71507a 100644 --- a/servatrice/src/smtp/qxtmailmessage.h +++ b/servatrice/src/smtp/qxtmailmessage.h @@ -64,7 +64,7 @@ public: void removeRecipient(const QString&); QHash extraHeaders() const; - QByteArray extraHeader(const QString&) const; + QString extraHeader(const QString&) const; bool hasExtraHeader(const QString&) const; void setExtraHeader(const QString& key, const QString& value); void setExtraHeaders(const QHash&); diff --git a/servatrice/src/smtp/qxtsmtp.cpp b/servatrice/src/smtp/qxtsmtp.cpp index 6326b101d..5ec59f0c7 100644 --- a/servatrice/src/smtp/qxtsmtp.cpp +++ b/servatrice/src/smtp/qxtsmtp.cpp @@ -23,44 +23,39 @@ ** ****************************************************************************/ + /*! * \class QxtSmtp * \inmodule QxtNetwork * \brief The QxtSmtp class implements the SMTP protocol for sending email */ + + #include "qxtsmtp.h" - -#include "qxthmac.h" #include "qxtsmtp_p.h" - -#include -#include +#include "qxthmac.h" #include #include +#include +#include QxtSmtpPrivate::QxtSmtpPrivate() : QObject(0) { // empty ctor } -QxtSmtp::QxtSmtp(QObject *parent) : QObject(parent) +QxtSmtp::QxtSmtp(QObject* parent) : QObject(parent) { QXT_INIT_PRIVATE(QxtSmtp); qxt_d().state = QxtSmtpPrivate::Disconnected; qxt_d().nextID = 0; qxt_d().socket = new QSslSocket(this); QObject::connect(socket(), SIGNAL(encrypted()), this, SIGNAL(encrypted())); - // QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo())); + //QObject::connect(socket(), SIGNAL(encrypted()), &qxt_d(), SLOT(ehlo())); QObject::connect(socket(), SIGNAL(connected()), this, SIGNAL(connected())); QObject::connect(socket(), SIGNAL(disconnected()), this, SIGNAL(disconnected())); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) - QObject::connect(socket(), SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &qxt_d(), - SLOT(socketError(QAbstractSocket::SocketError))); -#else - QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), - SLOT(socketError(QAbstractSocket::SocketError))); -#endif + QObject::connect(socket(), SIGNAL(error(QAbstractSocket::SocketError)), &qxt_d(), SLOT(socketError(QAbstractSocket::SocketError))); QObject::connect(this, SIGNAL(authenticated()), &qxt_d(), SLOT(sendNext())); QObject::connect(socket(), SIGNAL(readyRead()), &qxt_d(), SLOT(socketRead())); } @@ -70,7 +65,7 @@ QByteArray QxtSmtp::username() const return qxt_d().username; } -void QxtSmtp::setUsername(const QByteArray &username) +void QxtSmtp::setUsername(const QByteArray& username) { qxt_d().username = username; } @@ -80,12 +75,12 @@ QByteArray QxtSmtp::password() const return qxt_d().password; } -void QxtSmtp::setPassword(const QByteArray &password) +void QxtSmtp::setPassword(const QByteArray& password) { qxt_d().password = password; } -int QxtSmtp::send(const QxtMailMessage &message) +int QxtSmtp::send(const QxtMailMessage& message) { int messageID = ++qxt_d().nextID; qxt_d().pending.append(qMakePair(messageID, message)); @@ -99,19 +94,19 @@ int QxtSmtp::pendingMessages() const return qxt_d().pending.count(); } -QTcpSocket *QxtSmtp::socket() const +QTcpSocket* QxtSmtp::socket() const { return qxt_d().socket; } -void QxtSmtp::connectToHost(const QString &hostName, quint16 port) +void QxtSmtp::connectToHost(const QString& hostName, quint16 port) { qxt_d().useSecure = false; qxt_d().state = QxtSmtpPrivate::StartState; socket()->connectToHost(hostName, port); } -void QxtSmtp::connectToHost(const QHostAddress &address, quint16 port) +void QxtSmtp::connectToHost(const QHostAddress& address, quint16 port) { connectToHost(address.toString(), port); } @@ -131,141 +126,157 @@ void QxtSmtp::setStartTlsDisabled(bool disable) qxt_d().disableStartTLS = disable; } -QSslSocket *QxtSmtp::sslSocket() const +QSslSocket* QxtSmtp::sslSocket() const { return qxt_d().socket; } -void QxtSmtp::connectToSecureHost(const QString &hostName, quint16 port) +void QxtSmtp::connectToSecureHost(const QString& hostName, quint16 port) { qxt_d().useSecure = true; qxt_d().state = QxtSmtpPrivate::StartState; sslSocket()->connectToHostEncrypted(hostName, port); } -void QxtSmtp::connectToSecureHost(const QHostAddress &address, quint16 port) +void QxtSmtp::connectToSecureHost(const QHostAddress& address, quint16 port) { connectToSecureHost(address.toString(), port); } -bool QxtSmtp::hasExtension(const QString &extension) +bool QxtSmtp::hasExtension(const QString& extension) { return qxt_d().extensions.contains(extension); } -QString QxtSmtp::extensionData(const QString &extension) +QString QxtSmtp::extensionData(const QString& extension) { return qxt_d().extensions[extension]; } void QxtSmtpPrivate::socketError(QAbstractSocket::SocketError err) { - if (err == QAbstractSocket::SslHandshakeFailedError) { + if (err == QAbstractSocket::SslHandshakeFailedError) + { emit qxt_p().encryptionFailed(); - emit qxt_p().encryptionFailed(socket->errorString().toLatin1()); - } else if (state == StartState) { + emit qxt_p().encryptionFailed( socket->errorString().toLatin1() ); + } + else if (state == StartState) + { emit qxt_p().connectionFailed(); - emit qxt_p().connectionFailed(socket->errorString().toLatin1()); + emit qxt_p().connectionFailed( socket->errorString().toLatin1() ); } } void QxtSmtpPrivate::socketRead() { buffer += socket->readAll(); - while (true) { + while (true) + { int pos = buffer.indexOf("\r\n"); - if (pos < 0) - return; + if (pos < 0) return; QByteArray line = buffer.left(pos); buffer = buffer.mid(pos + 2); QByteArray code = line.left(3); - switch (state) { - case StartState: - if (code[0] != '2') { - socket->disconnectFromHost(); - } else { - ehlo(); - } - break; - case HeloSent: - case EhloSent: - case EhloGreetReceived: - parseEhlo(code, (line[3] != ' '), line.mid(4)); - break; - case StartTLSSent: - if (code == "220") { - socket->startClientEncryption(); - ehlo(); - } else { - authenticate(); - } - break; - case AuthRequestSent: - case AuthUsernameSent: - if (authType == AuthPlain) - authPlain(); - else if (authType == AuthLogin) - authLogin(); - else - authCramMD5(line.mid(4)); - break; - case AuthSent: - if (code[0] == '2') { - state = Authenticated; - emit qxt_p().authenticated(); - } else { - state = Disconnected; - emit qxt_p().authenticationFailed(); - emit qxt_p().authenticationFailed(line); - emit socket->disconnectFromHost(); - } - break; - case MailToSent: - case RcptAckPending: - if (code[0] != '2') { - emit qxt_p().mailFailed(pending.first().first, code.toInt()); - emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); - // pending.removeFirst(); - // DO NOT remove it, the body sent state needs this message to assigned the next mail failed message - // that will the sendNext a reset will be sent to clear things out - sendNext(); - state = BodySent; - } else - sendNextRcpt(code, line); - break; - case SendingBody: - sendBody(code, line); - break; - case BodySent: - if (pending.count()) { - // if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty, - // you will get into this state and then crash because no check is done. CHeck added but shouldnt - // be necessary since I commented out the removeFirst - if (code[0] != '2') { - emit qxt_p().mailFailed(pending.first().first, code.toInt()); - emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); - } else - emit qxt_p().mailSent(pending.first().first); - pending.removeFirst(); - } + switch (state) + { + case StartState: + if (code[0] != '2') + { + socket->disconnectFromHost(); + } + else + { + ehlo(); + } + break; + case HeloSent: + case EhloSent: + case EhloGreetReceived: + parseEhlo(code, (line[3] != ' '), line.mid(4)); + break; + case StartTLSSent: + if (code == "220") + { + socket->startClientEncryption(); + ehlo(); + } + else + { + authenticate(); + } + break; + case AuthRequestSent: + case AuthUsernameSent: + if (authType == AuthPlain) authPlain(); + else if (authType == AuthLogin) authLogin(); + else authCramMD5(line.mid(4)); + break; + case AuthSent: + if (code[0] == '2') + { + state = Authenticated; + emit qxt_p().authenticated(); + } + else + { + state = Disconnected; + emit qxt_p().authenticationFailed(); + emit qxt_p().authenticationFailed( line ); + emit socket->disconnectFromHost(); + } + break; + case MailToSent: + case RcptAckPending: + if (code[0] != '2') { + emit qxt_p().mailFailed( pending.first().first, code.toInt() ); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + // pending.removeFirst(); + // DO NOT remove it, the body sent state needs this message to assigned the next mail failed message that will + // the sendNext + // a reset will be sent to clear things out sendNext(); - break; - case Resetting: - if (code[0] != '2') { - emit qxt_p().connectionFailed(); - emit qxt_p().connectionFailed(line); - } else { - state = Waiting; - sendNext(); - } - break; - case Disconnected: - case EhloExtensionsReceived: - case EhloDone: - case Authenticated: - case Waiting: - // only to make compiler happy - break; + state = BodySent; + } + else + sendNextRcpt(code, line); + break; + case SendingBody: + sendBody(code, line); + break; + case BodySent: + if ( pending.count() ) + { + // if you removeFirst in RcpActpending/MailToSent on an error, and the queue is now empty, + // you will get into this state and then crash because no check is done. CHeck added but shouldnt + // be necessary since I commented out the removeFirst + if (code[0] != '2') + { + emit qxt_p().mailFailed(pending.first().first, code.toInt() ); + emit qxt_p().mailFailed(pending.first().first, code.toInt(), line); + } + else + emit qxt_p().mailSent(pending.first().first); + pending.removeFirst(); + } + sendNext(); + break; + case Resetting: + if (code[0] != '2') { + emit qxt_p().connectionFailed(); + emit qxt_p().connectionFailed( line ); + } + else { + state = Waiting; + sendNext(); + } + break; + case Disconnected: + case EhloExtensionsReceived: + case EhloDone: + case Authenticated: + case Waiting: + // only to make compiler happy + break; } } } @@ -273,7 +284,8 @@ void QxtSmtpPrivate::socketRead() void QxtSmtpPrivate::ehlo() { QByteArray address = "127.0.0.1"; - for (const QHostAddress &addr : QNetworkInterface::allAddresses()) { + foreach(const QHostAddress& addr, QNetworkInterface::allAddresses()) + { if (addr == QHostAddress::LocalHost || addr == QHostAddress::LocalHostIPv6) continue; address = addr.toString().toLatin1(); @@ -284,40 +296,53 @@ void QxtSmtpPrivate::ehlo() state = EhloSent; } -void QxtSmtpPrivate::parseEhlo(const QByteArray &code, bool cont, const QString &line) +void QxtSmtpPrivate::parseEhlo(const QByteArray& code, bool cont, const QString& line) { - if (code != "250") { + if (code != "250") + { // error! - if (state != HeloSent) { + if (state != HeloSent) + { // maybe let's try HELO socket->write("helo\r\n"); state = HeloSent; - } else { + } + else + { // nope socket->write("QUIT\r\n"); socket->flush(); socket->disconnectFromHost(); } return; - } else if (state != EhloGreetReceived) { - if (!cont) { + } + else if (state != EhloGreetReceived) + { + if (!cont) + { // greeting only, no extensions state = EhloDone; - } else { + } + else + { // greeting followed by extensions state = EhloGreetReceived; return; } - } else { + } + else + { extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1); if (!cont) state = EhloDone; } - if (state != EhloDone) - return; - if (extensions.contains("STARTTLS") && !disableStartTLS) { + if (state != EhloDone) return; + if (extensions.contains("STARTTLS") && !disableStartTLS) + { startTLS(); - } else { + } + else + { authenticate(); } } @@ -330,31 +355,44 @@ void QxtSmtpPrivate::startTLS() void QxtSmtpPrivate::authenticate() { - if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty()) { + if (!extensions.contains("AUTH") || username.isEmpty() || password.isEmpty()) + { state = Authenticated; emit qxt_p().authenticated(); - } else { - QStringList auth = extensions["AUTH"].toUpper().split(' ', Qt::SkipEmptyParts); - if (auth.contains("CRAM-MD5")) { + } + else + { + QStringList auth = extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts); + if (auth.contains("CRAM-MD5")) + { authCramMD5(); - } else if (auth.contains("PLAIN")) { + } + else if (auth.contains("PLAIN")) + { authPlain(); - } else if (auth.contains("LOGIN")) { + } + else if (auth.contains("LOGIN")) + { authLogin(); - } else { + } + else + { state = Authenticated; emit qxt_p().authenticated(); } } } -void QxtSmtpPrivate::authCramMD5(const QByteArray &challenge) +void QxtSmtpPrivate::authCramMD5(const QByteArray& challenge) { - if (state != AuthRequestSent) { + if (state != AuthRequestSent) + { socket->write("auth cram-md5\r\n"); authType = AuthCramMD5; state = AuthRequestSent; - } else { + } + else + { QxtHmac hmac(QCryptographicHash::Md5); hmac.setKey(password); hmac.addData(QByteArray::fromBase64(challenge)); @@ -366,11 +404,14 @@ void QxtSmtpPrivate::authCramMD5(const QByteArray &challenge) void QxtSmtpPrivate::authPlain() { - if (state != AuthRequestSent) { + if (state != AuthRequestSent) + { socket->write("auth plain\r\n"); authType = AuthPlain; state = AuthRequestSent; - } else { + } + else + { QByteArray auth; auth += '\0'; auth += username; @@ -383,44 +424,60 @@ void QxtSmtpPrivate::authPlain() void QxtSmtpPrivate::authLogin() { - if (state != AuthRequestSent && state != AuthUsernameSent) { + if (state != AuthRequestSent && state != AuthUsernameSent) + { socket->write("auth login\r\n"); authType = AuthLogin; state = AuthRequestSent; - } else if (state == AuthRequestSent) { + } + else if (state == AuthRequestSent) + { socket->write(username.toBase64() + "\r\n"); state = AuthUsernameSent; - } else { + } + else + { socket->write(password.toBase64() + "\r\n"); state = AuthSent; } } -static QByteArray qxt_extract_address(const QString &address) +static QByteArray qxt_extract_address(const QString& address) { int parenDepth = 0; int addrStart = -1; bool inQuote = false; int ct = address.length(); - for (int i = 0; i < ct; i++) { + for (int i = 0; i < ct; i++) + { QChar ch = address[i]; - if (inQuote) { + if (inQuote) + { if (ch == '"') inQuote = false; - } else if (addrStart != -1) { + } + else if (addrStart != -1) + { if (ch == '>') return address.mid(addrStart, (i - addrStart)).toLatin1(); - } else if (ch == '(') { + } + else if (ch == '(') + { parenDepth++; - } else if (ch == ')') { + } + else if (ch == ')') + { parenDepth--; - if (parenDepth < 0) - parenDepth = 0; - } else if (ch == '"') { + if (parenDepth < 0) parenDepth = 0; + } + else if (ch == '"') + { if (parenDepth == 0) inQuote = true; - } else if (ch == '<') { + } + else if (ch == '<') + { if (!inQuote && parenDepth == 0) addrStart = i + 1; } @@ -430,31 +487,35 @@ static QByteArray qxt_extract_address(const QString &address) void QxtSmtpPrivate::sendNext() { - if (state == Disconnected) { + if (state == Disconnected) + { // leave the mail in the queue if not ready to send return; } - if (pending.isEmpty()) { + if (pending.isEmpty()) + { // if there are no additional mails to send, finish up state = Waiting; emit qxt_p().finished(); return; } - if (state != Waiting) { + if(state != Waiting) { state = Resetting; socket->write("rset\r\n"); return; } - const QxtMailMessage &msg = pending.first().second; + const QxtMailMessage& msg = pending.first().second; rcptNumber = rcptAck = mailAck = 0; - recipients = - msg.recipients(QxtMailMessage::To) + msg.recipients(QxtMailMessage::Cc) + msg.recipients(QxtMailMessage::Bcc); - if (recipients.count() == 0) { + recipients = msg.recipients(QxtMailMessage::To) + + msg.recipients(QxtMailMessage::Cc) + + msg.recipients(QxtMailMessage::Bcc); + if (recipients.count() == 0) + { // can't send an e-mail with no recipients - emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients); - emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray("e-mail has no recipients")); + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients ); + emit qxt_p().mailFailed(pending.first().first, QxtSmtp::NoRecipients, QByteArray( "e-mail has no recipients" ) ); pending.removeFirst(); sendNext(); return; @@ -463,67 +524,87 @@ void QxtSmtpPrivate::sendNext() // interprets any string starting with an uppercase R as a request // to renegotiate the SSL connection. socket->write("mail from:<" + qxt_extract_address(msg.sender()) + ">\r\n"); - if (extensions.contains("PIPELINING")) // almost all do nowadays + if (extensions.contains("PIPELINING")) // almost all do nowadays { - for (const QString &rcpt : recipients) { + foreach(const QString& rcpt, recipients) + { socket->write("rcpt to:<" + qxt_extract_address(rcpt) + ">\r\n"); } state = RcptAckPending; - } else { + } + else + { state = MailToSent; } } -void QxtSmtpPrivate::sendNextRcpt(const QByteArray &code, const QByteArray &line) +void QxtSmtpPrivate::sendNextRcpt(const QByteArray& code, const QByteArray&line) { int messageID = pending.first().first; - const QxtMailMessage &msg = pending.first().second; + const QxtMailMessage& msg = pending.first().second; - if (code[0] != '2') { + if (code[0] != '2') + { // on failure, emit a warning signal - if (!mailAck) { + if (!mailAck) + { emit qxt_p().senderRejected(messageID, msg.sender()); - emit qxt_p().senderRejected(messageID, msg.sender(), line); - } else { + emit qxt_p().senderRejected(messageID, msg.sender(), line ); + } + else + { emit qxt_p().recipientRejected(messageID, msg.sender()); emit qxt_p().recipientRejected(messageID, msg.sender(), line); } - } else if (!mailAck) { + } + else if (!mailAck) + { mailAck = true; - } else { + } + else + { rcptAck++; } - if (rcptNumber == recipients.count()) { + if (rcptNumber == recipients.count()) + { // all recipients have been sent - if (rcptAck == 0) { + if (rcptAck == 0) + { // no recipients were considered valid - emit qxt_p().mailFailed(messageID, code.toInt()); + emit qxt_p().mailFailed(messageID, code.toInt() ); emit qxt_p().mailFailed(messageID, code.toInt(), line); pending.removeFirst(); sendNext(); - } else { + } + else + { // at least one recipient was acknowledged, send mail body socket->write("data\r\n"); state = SendingBody; } - } else if (state != RcptAckPending) { + } + else if (state != RcptAckPending) + { // send the next recipient unless we're only waiting on acks socket->write("rcpt to:<" + qxt_extract_address(recipients[rcptNumber]) + ">\r\n"); rcptNumber++; - } else { + } + else + { // If we're only waiting on acks, just count them rcptNumber++; } } -void QxtSmtpPrivate::sendBody(const QByteArray &code, const QByteArray &line) +void QxtSmtpPrivate::sendBody(const QByteArray& code, const QByteArray & line) { int messageID = pending.first().first; - const QxtMailMessage &msg = pending.first().second; + const QxtMailMessage& msg = pending.first().second; - if (code[0] != '3') { - emit qxt_p().mailFailed(messageID, code.toInt()); + if (code[0] != '3') + { + emit qxt_p().mailFailed(messageID, code.toInt() ); emit qxt_p().mailFailed(messageID, code.toInt(), line); pending.removeFirst(); sendNext(); diff --git a/servatrice/src/smtpclient.cpp b/servatrice/src/smtpclient.cpp index ee7214904..aa2f9198c 100644 --- a/servatrice/src/smtpclient.cpp +++ b/servatrice/src/smtpclient.cpp @@ -1,39 +1,37 @@ #include "smtpclient.h" - #include "settingscache.h" #include "smtp/qxtsmtp.h" -#include #include +#include -SmtpClient::SmtpClient(QObject *parent) : QObject(parent) +SmtpClient::SmtpClient(QObject *parent) +: QObject(parent) { smtp = new QxtSmtp(this); connect(smtp, SIGNAL(authenticated()), this, SLOT(authenticated())); - connect(smtp, SIGNAL(authenticationFailed(const QByteArray &)), this, - SLOT(authenticationFailed(const QByteArray &))); + connect(smtp, SIGNAL(authenticationFailed(const QByteArray &)), this, SLOT(authenticationFailed(const QByteArray &))); connect(smtp, SIGNAL(connected()), this, SLOT(connected())); connect(smtp, SIGNAL(connectionFailed(const QByteArray &)), this, SLOT(connectionFailed(const QByteArray &))); connect(smtp, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(smtp, SIGNAL(encrypted()), this, SLOT(encrypted())); connect(smtp, SIGNAL(encryptionFailed(const QByteArray &)), this, SLOT(encryptionFailed(const QByteArray &))); connect(smtp, SIGNAL(finished()), this, SLOT(finished())); - connect(smtp, SIGNAL(mailFailed(int, int, const QByteArray &)), this, - SLOT(mailFailed(int, int, const QByteArray &))); + connect(smtp, SIGNAL(mailFailed(int, int, const QByteArray &)), this, SLOT(mailFailed(int, int, const QByteArray &))); connect(smtp, SIGNAL(mailSent(int)), this, SLOT(mailSent(int))); - connect(smtp, SIGNAL(recipientRejected(int, const QString &, const QByteArray &)), this, - SLOT(recipientRejected(int, const QString &, const QByteArray &))); - connect(smtp, SIGNAL(senderRejected(int, const QString &, const QByteArray &)), this, - SLOT(senderRejected(int, const QString &, const QByteArray &))); + connect(smtp, SIGNAL(recipientRejected(int, const QString &, const QByteArray &)), this, SLOT(recipientRejected(int, const QString &, const QByteArray &))); + connect(smtp, SIGNAL(senderRejected(int, const QString &, const QByteArray &)), this, SLOT(senderRejected(int, const QString &, const QByteArray &))); } SmtpClient::~SmtpClient() { - if (smtp) { + if(smtp) + { delete smtp; smtp = 0; } + } bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token) @@ -43,70 +41,32 @@ bool SmtpClient::enqueueActivationTokenMail(const QString &nickname, const QStri QString subject = settingsCache->value("smtp/subject", "").toString(); QString body = settingsCache->value("smtp/body", "").toString(); - if (email.isEmpty()) { + if(email.isEmpty()) + { qDebug() << "[MAIL] Missing sender email in configuration"; return false; } - if (subject.isEmpty()) { + if(subject.isEmpty()) + { qDebug() << "[MAIL] Missing subject field in configuration"; return false; } - if (body.isEmpty()) { + if(body.isEmpty()) + { qDebug() << "[MAIL] Missing body field in configuration"; return false; } - if (recipient.isEmpty()) { + if(recipient.isEmpty()) + { qDebug() << "[MAIL] Missing recipient field for user " << nickname; return false; } - if (token.isEmpty()) { - qDebug() << "[MAIL] Missing token field for user " << nickname; - return false; - } - - QxtMailMessage message; - message.setSender(name + " <" + email + ">"); - message.addRecipient(recipient); - message.setSubject(subject); - message.setBody(body.replace("%username", nickname).replace("%token", token)); - - int id = smtp->send(message); - qDebug() << "[MAIL] Enqueued mail to" << recipient << "as" << id; - return true; -} - -bool SmtpClient::enqueueForgotPasswordTokenMail(const QString &nickname, const QString &recipient, const QString &token) -{ - QString email = settingsCache->value("smtp/email", "").toString(); - QString name = settingsCache->value("smtp/name", "").toString(); - QString subject = settingsCache->value("forgotpassword/subject", "").toString(); - QString body = settingsCache->value("forgotpassword/body", "").toString(); - - if (email.isEmpty()) { - qDebug() << "[MAIL] Missing sender email in configuration"; - return false; - } - - if (subject.isEmpty()) { - qDebug() << "[MAIL] Missing subject field in configuration"; - return false; - } - - if (body.isEmpty()) { - qDebug() << "[MAIL] Missing body field in configuration"; - return false; - } - - if (recipient.isEmpty()) { - qDebug() << "[MAIL] Missing recipient field for user " << nickname; - return false; - } - - if (token.isEmpty()) { + if(token.isEmpty()) + { qDebug() << "[MAIL] Missing token field for user " << nickname; return false; } @@ -125,10 +85,10 @@ bool SmtpClient::enqueueForgotPasswordTokenMail(const QString &nickname, const Q void SmtpClient::sendAllEmails() { // still connected from the previous round - if (smtp->socket()->state() == QAbstractSocket::ConnectedState) + if(smtp->socket()->state() == QAbstractSocket::ConnectedState) return; - if (smtp->pendingMessages() == 0) + if(smtp->pendingMessages() == 0) return; QString connectionType = settingsCache->value("smtp/connection", "tcp").toString(); @@ -142,8 +102,9 @@ void SmtpClient::sendAllEmails() smtp->setPassword(password); // Connect - if (connectionType == "ssl") { - if (acceptAllCerts) + if(connectionType == "ssl") + { + if(acceptAllCerts) smtp->sslSocket()->setPeerVerifyMode(QSslSocket::QueryPeer); smtp->connectToSecureHost(host, port); } else { @@ -156,7 +117,7 @@ void SmtpClient::authenticated() qDebug() << "[MAIL] authenticated"; } -void SmtpClient::authenticationFailed(const QByteArray &msg) +void SmtpClient::authenticationFailed(const QByteArray & msg) { qDebug() << "[MAIL] authenticationFailed" << QString(msg); } @@ -166,7 +127,7 @@ void SmtpClient::connected() qDebug() << "[MAIL] connected"; } -void SmtpClient::connectionFailed(const QByteArray &msg) +void SmtpClient::connectionFailed(const QByteArray & msg) { qDebug() << "[MAIL] connectionFailed" << QString(msg); } @@ -181,7 +142,7 @@ void SmtpClient::encrypted() qDebug() << "[MAIL] encrypted"; } -void SmtpClient::encryptionFailed(const QByteArray &msg) +void SmtpClient::encryptionFailed(const QByteArray & msg) { qDebug() << "[MAIL] encryptionFailed" << QString(msg); qDebug() << "[MAIL] Try enabling the \"acceptallcerts\" option in servatrice.ini"; @@ -193,7 +154,7 @@ void SmtpClient::finished() smtp->disconnectFromHost(); } -void SmtpClient::mailFailed(int mailID, int errorCode, const QByteArray &msg) +void SmtpClient::mailFailed(int mailID, int errorCode, const QByteArray & msg) { qDebug() << "[MAIL] mailFailed id=" << mailID << " errorCode=" << errorCode << "msg=" << QString(msg); } @@ -203,12 +164,12 @@ void SmtpClient::mailSent(int mailID) qDebug() << "[MAIL] mailSent" << mailID; } -void SmtpClient::recipientRejected(int mailID, const QString &address, const QByteArray &msg) +void SmtpClient::recipientRejected(int mailID, const QString & address, const QByteArray & msg) { qDebug() << "[MAIL] recipientRejected id=" << mailID << " address=" << address << "msg=" << QString(msg); } -void SmtpClient::senderRejected(int mailID, const QString &address, const QByteArray &msg) +void SmtpClient::senderRejected(int mailID, const QString & address, const QByteArray & msg) { qDebug() << "[MAIL] senderRejected id=" << mailID << " address=" << address << "msg=" << QString(msg); } diff --git a/servatrice/src/smtpclient.h b/servatrice/src/smtpclient.h index be97ed44d..b2b1ad002 100644 --- a/servatrice/src/smtpclient.h +++ b/servatrice/src/smtpclient.h @@ -6,32 +6,29 @@ class QxtSmtp; class QxtMailMessage; -class SmtpClient : public QObject -{ - Q_OBJECT +class SmtpClient : public QObject { + Q_OBJECT public: - SmtpClient(QObject *parent = 0); - ~SmtpClient(); - + SmtpClient(QObject *parent = 0); + ~SmtpClient(); protected: - QxtSmtp *smtp; + QxtSmtp *smtp; public slots: - bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); - bool enqueueForgotPasswordTokenMail(const QString &nickname, const QString &recipient, const QString &token); - void sendAllEmails(); + bool enqueueActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); + void sendAllEmails(); protected slots: - void authenticated(); - void authenticationFailed(const QByteArray &msg); - void connected(); - void connectionFailed(const QByteArray &msg); - void disconnected(); - void encrypted(); - void encryptionFailed(const QByteArray &msg); - void finished(); - void mailFailed(int mailID, int errorCode, const QByteArray &msg); - void mailSent(int mailID); - void recipientRejected(int mailID, const QString &address, const QByteArray &msg); - void senderRejected(int mailID, const QString &address, const QByteArray &msg); + void authenticated(); + void authenticationFailed(const QByteArray & msg); + void connected(); + void connectionFailed(const QByteArray & msg); + void disconnected(); + void encrypted(); + void encryptionFailed(const QByteArray & msg); + void finished(); + void mailFailed(int mailID, int errorCode, const QByteArray & msg); + void mailSent(int mailID); + void recipientRejected(int mailID, const QString & address, const QByteArray & msg); + void senderRejected(int mailID, const QString & address, const QByteArray & msg); }; #endif \ No newline at end of file diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 404e9e1b6..000000000 --- a/shell.nix +++ /dev/null @@ -1,32 +0,0 @@ -{ pkgs ? import {} }: - pkgs.mkShell { - nativeBuildInputs = with pkgs.buildPackages; [ - # Build tools - cmake - cmake-format - ninja - bash - curl - git - qtcreator - - # Debug / Test - valgrind - gdb - clang-tools - - # Compiler - gcc - - # Libraries - openssl - protobuf - qt6.qtbase - qt6.full - qt6.wrapQtAppsHook - ]; - - # Make debug builds work - # https://github.com/NixOS/nixpkgs/issues/18995 - hardeningDisable = [ "fortify" ]; - } diff --git a/sounds/CMakeLists.txt b/sounds/CMakeLists.txt new file mode 100644 index 000000000..80babdc3e --- /dev/null +++ b/sounds/CMakeLists.txt @@ -0,0 +1,19 @@ +# CMakeLists for sounds directory +# +# add sounds subfolders + +SET(defsounds + Default + Legacy +) + +if(UNIX) + if(APPLE) + INSTALL(DIRECTORY ${defsounds} DESTINATION cockatrice.app/Contents/Resources/sounds/) + else() + # Assume linux + INSTALL(DIRECTORY ${defsounds} DESTINATION share/cockatrice/sounds/) + endif() +elseif(WIN32) + INSTALL(DIRECTORY ${defsounds} DESTINATION sounds/) +endif() diff --git a/cockatrice/sounds/Default/attack_step.wav b/sounds/Default/attack_step.wav similarity index 100% rename from cockatrice/sounds/Default/attack_step.wav rename to sounds/Default/attack_step.wav diff --git a/cockatrice/sounds/Default/buddy_join.wav b/sounds/Default/buddy_join.wav similarity index 100% rename from cockatrice/sounds/Default/buddy_join.wav rename to sounds/Default/buddy_join.wav diff --git a/cockatrice/sounds/Default/buddy_leave.wav b/sounds/Default/buddy_leave.wav similarity index 100% rename from cockatrice/sounds/Default/buddy_leave.wav rename to sounds/Default/buddy_leave.wav diff --git a/cockatrice/sounds/Default/end_step.wav b/sounds/Default/end_step.wav similarity index 100% rename from cockatrice/sounds/Default/end_step.wav rename to sounds/Default/end_step.wav diff --git a/cockatrice/sounds/Default/player_join.wav b/sounds/Default/player_join.wav similarity index 100% rename from cockatrice/sounds/Default/player_join.wav rename to sounds/Default/player_join.wav diff --git a/cockatrice/sounds/Default/start_combat.wav b/sounds/Default/start_combat.wav similarity index 100% rename from cockatrice/sounds/Default/start_combat.wav rename to sounds/Default/start_combat.wav diff --git a/cockatrice/sounds/Default/tap_card.wav b/sounds/Default/tap_card.wav similarity index 100% rename from cockatrice/sounds/Default/tap_card.wav rename to sounds/Default/tap_card.wav diff --git a/cockatrice/sounds/Legacy/all_mention.wav b/sounds/Legacy/all_mention.wav similarity index 100% rename from cockatrice/sounds/Legacy/all_mention.wav rename to sounds/Legacy/all_mention.wav diff --git a/cockatrice/sounds/Legacy/chat_mention.wav b/sounds/Legacy/chat_mention.wav similarity index 100% rename from cockatrice/sounds/Legacy/chat_mention.wav rename to sounds/Legacy/chat_mention.wav diff --git a/cockatrice/sounds/Legacy/draw_step.wav b/sounds/Legacy/draw_step.wav similarity index 100% rename from cockatrice/sounds/Legacy/draw_step.wav rename to sounds/Legacy/draw_step.wav diff --git a/cockatrice/sounds/Legacy/play_card.wav b/sounds/Legacy/play_card.wav similarity index 100% rename from cockatrice/sounds/Legacy/play_card.wav rename to sounds/Legacy/play_card.wav diff --git a/cockatrice/sounds/Legacy/player_join.wav b/sounds/Legacy/player_join.wav similarity index 100% rename from cockatrice/sounds/Legacy/player_join.wav rename to sounds/Legacy/player_join.wav diff --git a/cockatrice/sounds/Legacy/private_message.wav b/sounds/Legacy/private_message.wav similarity index 100% rename from cockatrice/sounds/Legacy/private_message.wav rename to sounds/Legacy/private_message.wav diff --git a/cockatrice/sounds/Legacy/shuffle.wav b/sounds/Legacy/shuffle.wav similarity index 100% rename from cockatrice/sounds/Legacy/shuffle.wav rename to sounds/Legacy/shuffle.wav diff --git a/cockatrice/sounds/Legacy/tap_card.wav b/sounds/Legacy/tap_card.wav similarity index 100% rename from cockatrice/sounds/Legacy/tap_card.wav rename to sounds/Legacy/tap_card.wav diff --git a/cockatrice/sounds/Legacy/untap_card.wav b/sounds/Legacy/untap_card.wav similarity index 100% rename from cockatrice/sounds/Legacy/untap_card.wav rename to sounds/Legacy/untap_card.wav diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fffaf1bda..93deb7df5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,69 +1,38 @@ -# NOTE: Qt modules for tests are defined centrally in cmake/FindQtRuntime.cmake (the _TEST_NEEDED variable). -# If a new test needs additional Qt modules, add them there — not in individual test CMakeLists.txt files. enable_testing() - add_test(NAME dummy_test COMMAND dummy_test) -add_test(NAME expression_test COMMAND expression_test) -add_test(NAME test_age_formatting COMMAND test_age_formatting) -add_test(NAME password_hash_test COMMAND password_hash_test) - -add_test(NAME deck_hash_performance_test COMMAND deck_hash_performance_test) -set_tests_properties(deck_hash_performance_test PROPERTIES TIMEOUT 5) # Find GTest add_executable(dummy_test dummy_test.cpp) -add_executable(expression_test expression_test.cpp) -add_executable(test_age_formatting test_age_formatting.cpp) -add_executable(password_hash_test password_hash_test.cpp) -add_executable(deck_hash_performance_test deck_hash_performance_test.cpp) find_package(GTest) if(NOT GTEST_FOUND) - if(NOT EXISTS "${CMAKE_BINARY_DIR}/gtest-build") - message(STATUS "Downloading googletest") - configure_file( - "${CMAKE_SOURCE_DIR}/cmake/gtest-CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/gtest-download/CMakeLists.txt" - ) - execute_process( - COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download - ) - execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download) - else() - message(STATUS "GoogleTest directory exists") - endif() + IF(NOT EXISTS "${CMAKE_BINARY_DIR}/gtest-build") + message(STATUS "Downloading googletest") + configure_file("${CMAKE_SOURCE_DIR}/cmake/gtest-CMakeLists.txt.in" "${CMAKE_BINARY_DIR}/gtest-download/CMakeLists.txt") + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/gtest-download ) + ELSE() + message(STATUS "GoogleTest directory exists") + ENDIF() - # Add gtest directly to our build - add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src ${CMAKE_BINARY_DIR}/gtest-build EXCLUDE_FROM_ALL) + # Add gtest directly to our build + add_subdirectory(${CMAKE_BINARY_DIR}/gtest-src + ${CMAKE_BINARY_DIR}/gtest-build + EXCLUDE_FROM_ALL ) - # Add the gtest include directory, since gtest - # doesn't add that dependency to its gtest target - target_include_directories(gtest INTERFACE "$") + # Add the gtest include directory, since gtest + # doesn't add that dependency to its gtest target + target_include_directories(gtest INTERFACE + "${CMAKE_BINARY_DIR}/gtest-src/include" ) - set(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include") - set(GTEST_BOTH_LIBRARIES gtest) - add_dependencies(dummy_test gtest) - add_dependencies(expression_test gtest) - add_dependencies(test_age_formatting gtest) - add_dependencies(password_hash_test gtest) - add_dependencies(deck_hash_performance_test gtest) + SET(GTEST_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/gtest-src/include") + SET(GTEST_BOTH_LIBRARIES gtest) + add_dependencies(dummy_test gtest) endif() include_directories(${GTEST_INCLUDE_DIRS}) -target_link_libraries(dummy_test Threads::Threads ${GTEST_BOTH_LIBRARIES}) -target_link_libraries(expression_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) -target_link_libraries(test_age_formatting Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) -target_link_libraries( - password_hash_test libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES} -) -target_link_libraries( - deck_hash_performance_test libcockatrice_deck_list libcockatrice_utility Threads::Threads ${GTEST_BOTH_LIBRARIES} - ${TEST_QT_MODULES} -) - -add_subdirectory(card_zone_algorithms) -add_subdirectory(carddatabase) -add_subdirectory(loading_from_clipboard) -add_subdirectory(movecard_tests) -add_subdirectory(oracle) +target_link_libraries(dummy_test ${GTEST_BOTH_LIBRARIES}) \ No newline at end of file diff --git a/tests/card_zone_algorithms/CMakeLists.txt b/tests/card_zone_algorithms/CMakeLists.txt deleted file mode 100644 index 889e92eaa..000000000 --- a/tests/card_zone_algorithms/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -add_executable(card_zone_algorithms_test card_zone_algorithms_test.cpp) - -target_include_directories(card_zone_algorithms_test PRIVATE ${CMAKE_SOURCE_DIR}/cockatrice/src/game/zones/logic) - -target_link_libraries( - card_zone_algorithms_test - PRIVATE Threads::Threads - PRIVATE ${GTEST_BOTH_LIBRARIES} -) - -add_test(NAME card_zone_algorithms_test COMMAND card_zone_algorithms_test) - -if(NOT GTEST_FOUND) - add_dependencies(card_zone_algorithms_test gtest) -endif() diff --git a/tests/card_zone_algorithms/card_zone_algorithms_test.cpp b/tests/card_zone_algorithms/card_zone_algorithms_test.cpp deleted file mode 100644 index cc098cae9..000000000 --- a/tests/card_zone_algorithms/card_zone_algorithms_test.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "card_zone_algorithms.h" - -#include -#include - -struct MockCardRef -{ -}; - -struct MockCard -{ - int idSet = 0; - bool idWasCalled = false; - MockCardRef cardRefSet{}; - bool cardRefWasCalled = false; - bool resetStateCalled = false; - bool resetStateKeepAnnotations = false; - bool visibleSet = false; - - void setId(int id) - { - idSet = id; - idWasCalled = true; - } - - void setCardRef(MockCardRef ref) - { - cardRefSet = ref; - cardRefWasCalled = true; - } - - void resetState(bool keepAnnotations) - { - resetStateCalled = true; - resetStateKeepAnnotations = keepAnnotations; - } - - void setVisible(bool visible) - { - visibleSet = visible; - } -}; - -class MockCardList -{ - std::vector cards; - bool contentsKnown; - -public: - explicit MockCardList(bool _contentsKnown) : contentsKnown(_contentsKnown) - { - } - - int size() const - { - return static_cast(cards.size()); - } - - void insert(int index, MockCard *card) - { - cards.insert(cards.begin() + index, card); - } - - bool getContentsKnown() const - { - return contentsKnown; - } - - MockCard *at(int index) const - { - return cards.at(index); - } -}; - -class AddCardAlgorithmTest : public ::testing::Test -{ -protected: - MockCardList knownList{true}; - MockCardList unknownList{false}; -}; - -TEST_F(AddCardAlgorithmTest, NegativeIndexClampsToEnd) -{ - MockCard a, b; - CardZoneAlgorithms::addCardToList(knownList, &a, 0, false); - CardZoneAlgorithms::addCardToList(knownList, &b, -1, false); - - EXPECT_EQ(knownList.at(0), &a); - EXPECT_EQ(knownList.at(1), &b); - EXPECT_EQ(knownList.size(), 2); -} - -TEST_F(AddCardAlgorithmTest, IndexBeyondSizeClampsToEnd) -{ - MockCard a, b; - CardZoneAlgorithms::addCardToList(knownList, &a, 0, false); - CardZoneAlgorithms::addCardToList(knownList, &b, 999, false); - - EXPECT_EQ(knownList.at(0), &a); - EXPECT_EQ(knownList.at(1), &b); - EXPECT_EQ(knownList.size(), 2); -} - -TEST_F(AddCardAlgorithmTest, ContentsKnownPreservesIdentity) -{ - MockCard card; - CardZoneAlgorithms::addCardToList(knownList, &card, 0, false); - - EXPECT_FALSE(card.idWasCalled); - EXPECT_FALSE(card.cardRefWasCalled); - EXPECT_TRUE(card.visibleSet); -} - -TEST_F(AddCardAlgorithmTest, ContentsUnknownClearsIdentity) -{ - MockCard card; - CardZoneAlgorithms::addCardToList(unknownList, &card, 0, false); - - EXPECT_TRUE(card.idWasCalled); - EXPECT_EQ(card.idSet, -1); - EXPECT_TRUE(card.cardRefWasCalled); -} - -TEST_F(AddCardAlgorithmTest, MidListInsertionPreservesOrder) -{ - MockCard a, b, c; - CardZoneAlgorithms::addCardToList(knownList, &a, 0, false); - CardZoneAlgorithms::addCardToList(knownList, &b, 1, false); - CardZoneAlgorithms::addCardToList(knownList, &c, 1, false); - - EXPECT_EQ(knownList.size(), 3); - EXPECT_EQ(knownList.at(0), &a); - EXPECT_EQ(knownList.at(1), &c); - EXPECT_EQ(knownList.at(2), &b); -} - -TEST_F(AddCardAlgorithmTest, KeepAnnotationsFalsePassedThrough) -{ - MockCard card; - CardZoneAlgorithms::addCardToList(knownList, &card, 0, false); - - EXPECT_TRUE(card.resetStateCalled); - EXPECT_FALSE(card.resetStateKeepAnnotations); -} - -TEST_F(AddCardAlgorithmTest, KeepAnnotationsTruePassedThrough) -{ - MockCard card; - CardZoneAlgorithms::addCardToList(knownList, &card, 0, true); - - EXPECT_TRUE(card.resetStateCalled); - EXPECT_TRUE(card.resetStateKeepAnnotations); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/carddatabase/CMakeLists.txt b/tests/carddatabase/CMakeLists.txt deleted file mode 100644 index 987e23cd5..000000000 --- a/tests/carddatabase/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(CardDatabaseTests VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") - -# ------------------------ -# Definitions -# ------------------------ -add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"") - -# ------------------------ -# Card Database Test -# ------------------------ -add_executable(carddatabase_test ${MOCKS_SOURCES} ${VERSION_STRING_CPP} carddatabase_test.cpp mocks.cpp) - -target_link_libraries( - carddatabase_test - PRIVATE libcockatrice_card - PRIVATE Threads::Threads - PRIVATE ${GTEST_BOTH_LIBRARIES} - PRIVATE ${TEST_QT_MODULES} -) - -add_test(NAME carddatabase_test COMMAND carddatabase_test) - -# ------------------------ -# Filter String Test -# (guard must match the condition for libcockatrice_filters in the root CMakeLists.txt) -# ------------------------ -if(WITH_ORACLE OR WITH_CLIENT) - add_executable(filter_string_test ${MOCKS_SOURCES} ${VERSION_STRING_CPP} filter_string_test.cpp mocks.cpp) - - target_link_libraries( - filter_string_test - PRIVATE libcockatrice_filters - PRIVATE Threads::Threads - PRIVATE ${GTEST_BOTH_LIBRARIES} - PRIVATE ${TEST_QT_MODULES} - ) - - add_test(NAME filter_string_test COMMAND filter_string_test) - - if(NOT GTEST_FOUND) - add_dependencies(filter_string_test gtest) - endif() -endif() - -# ------------------------ -# Dependencies on gtest -# ------------------------ -if(NOT GTEST_FOUND) - add_dependencies(carddatabase_test gtest) -endif() diff --git a/tests/carddatabase/carddatabase_test.cpp b/tests/carddatabase/carddatabase_test.cpp deleted file mode 100644 index 3fa0e3834..000000000 --- a/tests/carddatabase/carddatabase_test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "mocks.h" -#include "test_card_database_path_provider.h" - -#include "gtest/gtest.h" -#include -#include - -namespace -{ - -TEST(CardDatabaseTest, LoadXml) -{ - CardDatabase *db = new CardDatabase(nullptr, new NoopCardPreferenceProvider(), new TestCardDatabasePathProvider(), - new NoopCardSetPriorityController()); - - // ensure the card database is empty at start - ASSERT_EQ(0, db->getCardList().size()) << "Cards not empty at start"; - ASSERT_EQ(0, db->getSetList().size()) << "Sets not empty at start"; - ASSERT_EQ(0, db->query()->getAllMainCardTypes().size()) << "Types not empty at start"; - ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status at start"; - - // load dummy cards and test result - db->loadCardDatabases(); - ASSERT_EQ(9, db->getCardList().size()) << "Wrong card count after load"; - ASSERT_EQ(5, db->getSetList().size()) << "Wrong sets count after load"; - ASSERT_EQ(3, db->query()->getAllMainCardTypes().size()) << "Wrong types count after load"; - ASSERT_EQ(Ok, db->getLoadStatus()) << "Wrong status after load"; - - // ensure the card database is empty after clear() - db->clear(); - ASSERT_EQ(0, db->getCardList().size()) << "Cards not empty after clear"; - ASSERT_EQ(0, db->getSetList().size()) << "Sets not empty after clear"; - ASSERT_EQ(0, db->query()->getAllMainCardTypes().size()) << "Types not empty after clear"; - ASSERT_EQ(NotLoaded, db->getLoadStatus()) << "Incorrect status after clear"; -} -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/tests/carddatabase/data/cards.xml b/tests/carddatabase/data/cards.xml deleted file mode 100644 index 2c1c09ed8..000000000 --- a/tests/carddatabase/data/cards.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - Cat - CAT - 0 - Meow! - - 111 - G - 2G - 2 - Creature — Cat - Creature - 3/3 - - - - Dog - DOG - 0 - Woof! - - 222 - R - 2RR - 4 - Creature — Dog - Creature - 4/4 - - - - Doctor - WHO - 0 - Why did wizards introduce two-word creature types - - 222 - R - 2RR - 4 - Creature — Human Time Lord Doctor - Creature - 4/4 - - - - Not Dead - Not a Card - 0 - Dead! - - 333 - B - B - 1 - Instant - - - - Truth - Not a Card - 0 - Truth! - - 444 - U - 2U - 2 - Instant - - - - diff --git a/tests/carddatabase/data/customsets/customset1.xml b/tests/carddatabase/data/customsets/customset1.xml deleted file mode 100644 index b2f187d54..000000000 --- a/tests/carddatabase/data/customsets/customset1.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Sparrow - BRD - W - W - 1 - Creature - 1/1 - 0 - - - - Crow - BRD - B - 1B - 2 - Creature - 2/2 - 0 - - - - diff --git a/tests/carddatabase/data/spoilers.xml b/tests/carddatabase/data/spoilers.xml deleted file mode 100644 index dacd358b3..000000000 --- a/tests/carddatabase/data/spoilers.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Fluffy - CAT - 0 - - 1 - - 311 - G - - - Token - Token - 0/1 - - - - diff --git a/tests/carddatabase/data/tokens.xml b/tests/carddatabase/data/tokens.xml deleted file mode 100644 index 41b376153..000000000 --- a/tests/carddatabase/data/tokens.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Kitten - CAT - 0 - - 1 - - 112 - G - - - Token - Token - 1/1 - - - - Puppy - DOG - 0 - - 1 - - 223 - R - - - Token - Token - 1/1 - - - - diff --git a/tests/carddatabase/filter_string_test.cpp b/tests/carddatabase/filter_string_test.cpp deleted file mode 100644 index c6d68be1f..000000000 --- a/tests/carddatabase/filter_string_test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "mocks.h" -#include "test_card_database_path_provider.h" - -#include "gtest/gtest.h" -#include -#include -#include - -#define QUERY(name, card, query, match) \ - TEST_F(CardQuery, name) \ - { \ - ASSERT_EQ(FilterString(query).check(card), match); \ - } - -namespace -{ - -class CardQuery : public ::testing::Test -{ -protected: - void SetUp() override - { - CardDatabase *db = new CardDatabase(nullptr, new NoopCardPreferenceProvider(), - new TestCardDatabasePathProvider(), new NoopCardSetPriorityController()); - db->loadCardDatabases(); - - cat = db->query()->getCardBySimpleName("Cat"); - notDeadAfterAll = db->query()->getCardBySimpleName("Not Dead"); - truth = db->query()->getCardBySimpleName("Truth"); - doctor = db->query()->getCardBySimpleName("Doctor"); - } - // void TearDown() override {} - - CardData cat; - CardData notDeadAfterAll; - CardData truth; - CardData doctor; -}; - -QUERY(Empty, cat, "", true) -QUERY(Typing, cat, "t", true) - -QUERY(NonMatchingType, cat, "t:kithkin", false) -QUERY(MatchingType, cat, "t:creature", true) -QUERY(MatchingCreatureType, cat, "t:cat", true) -QUERY(PartialMatchingType, cat, "t:ca", false) -QUERY(MatchingMultiWordType, doctor, "t:\"Time Lord\"", true) -QUERY(Not1, cat, "NOT t:kithkin", true) -QUERY(Not2, cat, "NOT t:creature", false) -QUERY(NonKeyword1, cat, "not t:kithkin", false) -QUERY(NonKeyword2, cat, "t:bat or t:creature", false) -QUERY(NonKeyword3, notDeadAfterAll, "not dead", true) -QUERY(NonKeyword4, truth, "truth or trail", false) -QUERY(Case, cat, "t:cReAtUrE", true) - -QUERY(And, cat, "t:creature t:creature", true) -QUERY(And2, cat, "t:creature t:sorcery", false) - -QUERY(Or, cat, "t:bat OR t:creature", true) - -QUERY(Cmc1, cat, "cmc=2", true) -QUERY(Cmc2, cat, "cmc>3", false) -QUERY(Cmc3, cat, "cmc>1", true) - -QUERY(Quotes, cat, "t:\"creature\"", true) - -QUERY(Field, cat, "pt:\"3/3\"", true) - -QUERY(Color1, cat, "c:g", true) -QUERY(Color2, cat, "c:gw", true) -QUERY(Color3, cat, "c!g", true) -QUERY(Color4, cat, "c!gw", false) - -QUERY(BracketNextToUnquotedString, cat, "(o:woof OR o:meow)", true) - -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp deleted file mode 100644 index 5568ac84f..000000000 --- a/tests/carddatabase/mocks.cpp +++ /dev/null @@ -1,6 +0,0 @@ - -#include "mocks.h" - -void CardPictureLoader::clearPixmapCache(CardInfoPtr /* card */) -{ -} diff --git a/tests/carddatabase/mocks.h b/tests/carddatabase/mocks.h deleted file mode 100644 index 016642005..000000000 --- a/tests/carddatabase/mocks.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Beware of this preprocessor hack used to redefine the settingCache class - * instead of including it and all of its dependencies. - * Always set header guards of mocked objects before including any headers - * with mocked objects. - */ - -#define PICTURELOADER_H - -#include - -class CardPictureLoader -{ -public: - static void clearPixmapCache(CardInfoPtr card); -}; diff --git a/tests/carddatabase/test_card_database_path_provider.h b/tests/carddatabase/test_card_database_path_provider.h deleted file mode 100644 index 8fc8ef962..000000000 --- a/tests/carddatabase/test_card_database_path_provider.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H -#define COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H - -#include - -class TestCardDatabasePathProvider : public ICardDatabasePathProvider -{ - -public: - QString getCardDatabasePath() const override - { - return QString("%1/cards.xml").arg(CARDDB_DATADIR); - } - QString getCustomCardDatabasePath() const override - { - return QString("%1/customsets/").arg(CARDDB_DATADIR); - } - QString getTokenDatabasePath() const override - { - return QString("%1/tokens.xml").arg(CARDDB_DATADIR); - } - QString getSpoilerCardDatabasePath() const override - { - return QString("%1/spoiler.xml").arg(CARDDB_DATADIR); - } -}; - -#endif // COCKATRICE_TEST_CARD_DATABASE_PATH_PROVIDER_H diff --git a/tests/deck_hash_performance_test.cpp b/tests/deck_hash_performance_test.cpp deleted file mode 100644 index 154283e8e..000000000 --- a/tests/deck_hash_performance_test.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "gtest/gtest.h" -#include -#include - -static constexpr int amount = 1e5; -QString repeatDeck; -QString numberDeck; -QString uniquesDeck; -QString uniquesXorDeck; -QString duplicatesDeck; - -TEST(DeckHashTest, RepeatTest) -{ - DeckList decklist(repeatDeck); - for (int i = 0; i < amount; ++i) { - decklist.getDeckHash(); - decklist.refreshDeckHash(); - } - auto hash = decklist.getDeckHash().toStdString(); - ASSERT_EQ(hash, "5cac19qm") << "The hash does not match!"; -} - -TEST(DeckHashTest, NumberTest) -{ - DeckList decklist(numberDeck); - auto hash = decklist.getDeckHash().toStdString(); - ASSERT_EQ(hash, "e0m38p19") << "The hash does not match!"; -} - -TEST(DeckHashTest, UniquesTest) -{ - DeckList decklist(uniquesDeck); - auto hash = decklist.getDeckHash().toStdString(); - ASSERT_EQ(hash, "88prk025") << "The hash does not match!"; -} - -TEST(DeckHashTest, UniquesTestXor) -{ - DeckList decklist(uniquesXorDeck); - auto hash = decklist.getDeckHash().toStdString(); - ASSERT_EQ(hash, "hkn6q4pf") << "The hash does not match!"; -} - -TEST(DeckHashTest, DuplicatesTest) -{ - DeckList decklist(duplicatesDeck); - auto hash = decklist.getDeckHash().toStdString(); - ASSERT_EQ(hash, "ekt6tg1h") << "The hash does not match!"; -} - -int main(int argc, char **argv) -{ - const QString deckStart = - R"()"; - const QString deckEnd = R"()"; - - repeatDeck = - deckStart + - R"()" + - deckEnd; - numberDeck = deckStart + QString(R"()").arg(amount) + deckEnd; - - QStringList deckString{deckStart}; - QStringList deckStringXor = deckString; - int len = QString::number(amount).length(); - for (int i = 0; i < amount; ++i) { - // creates already sorted list - deckString << R"()"; - // xor in order to mess with sorting - deckStringXor << R"()"; - } - deckString << deckEnd; - deckStringXor << deckEnd; - uniquesDeck = deckString.join(""); - uniquesXorDeck = deckStringXor.join(""); - - duplicatesDeck = deckStart + QString(R"()").repeated(amount) + deckEnd; - - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/dummy_test.cpp b/tests/dummy_test.cpp index 31a01bcff..a5416d092 100644 --- a/tests/dummy_test.cpp +++ b/tests/dummy_test.cpp @@ -1,19 +1,16 @@ #include "gtest/gtest.h" -namespace -{ -class FooTest : public ::testing::Test -{ -}; +namespace { + class FooTest : public ::testing::Test { -TEST(DummyTest, Works) -{ - ASSERT_EQ(1, 1) << "One is not equal to one"; + }; + + TEST(DummyTest, Works) { + ASSERT_EQ(1, 1) << "One is not equal to one"; + } } -} // namespace -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/tests/expression_test.cpp b/tests/expression_test.cpp deleted file mode 100644 index 4fbe98cb9..000000000 --- a/tests/expression_test.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "gtest/gtest.h" -#include -#include - -#define TEST_EXPR(name, a, b) \ - TEST(ExpressionTest, name) \ - { \ - Expression exp(8); \ - ASSERT_EQ(exp.parse(a), b) << a; \ - } - -namespace -{ - -TEST_EXPR(Number, "1", 1) -TEST_EXPR(Multiply, "2*2", 4) -TEST_EXPR(Whitespace, "3 * 3", 9) -TEST_EXPR(Powers, "2^8", 256) -TEST_EXPR(OrderOfOperations, "2+2*2", 6) -TEST_EXPR(Fn, "2*cos(1)", 2 * qCos(1)) -TEST_EXPR(Variable, "x / 2", 4) -TEST_EXPR(Negative, "-2 * 2", -4) -TEST_EXPR(UnknownFnReturnsZero, "blah(22)", 0) - -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/loading_from_clipboard/CMakeLists.txt b/tests/loading_from_clipboard/CMakeLists.txt deleted file mode 100644 index 719d62f45..000000000 --- a/tests/loading_from_clipboard/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -add_definitions("-DCARDDB_DATADIR=\"${CMAKE_CURRENT_SOURCE_DIR}/data/\"") -add_executable(loading_from_clipboard_test ${VERSION_STRING_CPP} clipboard_testing.cpp loading_from_clipboard_test.cpp) - -if(NOT GTEST_FOUND) - add_dependencies(loading_from_clipboard_test gtest) -endif() - -target_link_libraries( - loading_from_clipboard_test libcockatrice_deck_list libcockatrice_card Threads::Threads ${GTEST_BOTH_LIBRARIES} - ${TEST_QT_MODULES} -) -add_test(NAME loading_from_clipboard_test COMMAND loading_from_clipboard_test) diff --git a/tests/loading_from_clipboard/clipboard_testing.cpp b/tests/loading_from_clipboard/clipboard_testing.cpp deleted file mode 100644 index a9977b800..000000000 --- a/tests/loading_from_clipboard/clipboard_testing.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "clipboard_testing.h" - -#include -#include -#include - -DeckList getDeckList(const QString &clipboard) -{ - DeckList deckList; - QString cp(clipboard); - QTextStream stream(&cp); // text stream requires local copy - deckList.loadFromStream_Plain(stream, false, CardNameNormalizer()); - return deckList; -} - -void testEmpty(const QString &clipboard) -{ - DeckList deckList = getDeckList(clipboard); - - ASSERT_TRUE(deckList.getCardList().isEmpty()); -} - -void testHash(const QString &clipboard, const std::string &hash) -{ - DeckList deckList = getDeckList(clipboard); - - ASSERT_EQ(deckList.getDeckHash().toStdString(), hash); -} - -void testDeck(const QString &clipboard, const Result &result) -{ - DeckList deckList = getDeckList(clipboard); - - ASSERT_EQ(result.name, deckList.getName().toStdString()); - ASSERT_EQ(result.comments, deckList.getComments().toStdString()); - - CardRows mainboard; - CardRows sideboard; - - auto extractCards = [&mainboard, &sideboard](const InnerDecklistNode *innerDecklistNode, - const DecklistCardNode *card) { - if (innerDecklistNode->getName() == DECK_ZONE_MAIN) { - mainboard.append({card->getName().toStdString(), card->getNumber()}); - } else if (innerDecklistNode->getName() == DECK_ZONE_SIDE) { - sideboard.append({card->getName().toStdString(), card->getNumber()}); - } else { - FAIL(); - } - }; - - deckList.forEachCard(extractCards); - - ASSERT_EQ(result.mainboard, mainboard); - ASSERT_EQ(result.sideboard, sideboard); -} diff --git a/tests/loading_from_clipboard/clipboard_testing.h b/tests/loading_from_clipboard/clipboard_testing.h deleted file mode 100644 index 41d8ea794..000000000 --- a/tests/loading_from_clipboard/clipboard_testing.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CLIPBOARD_TESTING_H -#define CLIPBOARD_TESTING_H - -#include "gtest/gtest.h" -#include - -// using std types because qt types aren't understood by gtest (without this you'll get less nice errors) -using CardRows = QVector>; - -struct Result -{ - std::string name; - std::string comments; - CardRows mainboard; - CardRows sideboard; - - Result(std::string _name, std::string _comments, CardRows _mainboard, CardRows _sideboard) - : name(_name), comments(_comments), mainboard(_mainboard), sideboard(_sideboard) - { - } -}; - -void testEmpty(const QString &clipboard); -void testHash(const QString &clipboard, const std::string &hash); -void testDeck(const QString &clipboard, const Result &result); - -#endif // CLIPBOARD_TESTING_H diff --git a/tests/loading_from_clipboard/loading_from_clipboard_test.cpp b/tests/loading_from_clipboard/loading_from_clipboard_test.cpp deleted file mode 100644 index fcfbb22db..000000000 --- a/tests/loading_from_clipboard/loading_from_clipboard_test.cpp +++ /dev/null @@ -1,226 +0,0 @@ -#include "clipboard_testing.h" - -// Testing is done by using the DeckList::loadFromString_Plain function in common/decklist.h -// It does not check if cards are in the database at all, so no comparisons to the database will be made. - -TEST(LoadingFromClipboardTest, EmptyDeck) -{ - testEmpty(""); -} - -TEST(LoadingFromClipboardTest, EmptySideboard) -{ - testEmpty("Sideboard"); -} - -TEST(LoadingFromClipboardTest, QuantityPrefixed) -{ - QString clipboard("1 Mountain\n" - "2x Island\n" - "3x Forest\n"); - Result result("", "", {{"Mountain", 1}, {"Island", 2}, {"Forest", 3}}, {}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, CommentsAreIgnored) -{ - QString clipboard("//1 Mountain\n" - "//2x Island\n" - "//SB:2x Island\n"); - testEmpty(clipboard); -} - -TEST(LoadingFromClipboardTest, SideboardPrefix) -{ - QString clipboard("1 Mountain\n" - "SB: 1 Mountain\n" - "sb: 2x Island\n" - "2 Swamp\n" - "\n" - "3 Plains\n"); - Result result("", "", {{"Mountain", 1}, {"Swamp", 2}, {"Plains", 3}}, {{"Mountain", 1}, {"Island", 2}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, SideboardLine) -{ - QString clipboard("1 Mountain\n" - "2 Swamp\n" - "\n" - "3 Plains\n" - "sideboard\n" - "1 Mountain\n" - "2x Island\n"); - Result result("", "", {{"Mountain", 1}, {"Swamp", 2}, {"Plains", 3}}, {{"Mountain", 1}, {"Island", 2}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, UnknownCardsAreNotDiscarded) -{ - QString clipboard("1 CardThatDoesNotExistInCardsXml\n"); - Result result("", "", {{"CardThatDoesNotExistInCardsXml", 1}}, {}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, WeirdWhitespaceIsIgnored) -{ - QString clipboard( - "\t\tSb:\t1\tOur Market Research Shows That Players Like Really Long Card Names So We Made " - " This Card to Have\tthe Absolute \t Longest Card Name \tEver Elemental\t\n\t"); - Result result("", "", {}, - {{"Our Market Research Shows That Players Like Really Long Card Names So We Made This Card to Have " - "the Absolute Longest Card Name Ever Elemental", - 1}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, RemoveBlankEntriesFromBeginningAndEnd) -{ - QString clipboard("\n" - "\n" - "\n" - "1x Algae Gharial\n" - "3x CardThatDoesNotExistInCardsXml\n" - "2x Phelddagrif\n" - "\n" - "\n"); - - Result result("", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}, {"Phelddagrif", 2}}, {}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, UseFirstBlankIfOnlyOneBlankToSplitSideboard) -{ - QString clipboard("1x Algae Gharial\n" - "3x CardThatDoesNotExistInCardsXml\n" - "\n" - "2x Phelddagrif\n"); - - Result result("", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}}, {{"Phelddagrif", 2}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, IfMultipleScatteredBlanksAllMainBoard) -{ - QString clipboard("1x Algae Gharial\n" - "3x CardThatDoesNotExistInCardsXml\n" - "\n" - "2x Phelddagrif\n" - "\n" - "3 Giant Growth\n"); - - Result result( - "", "", {{"Algae Gharial", 1}, {"CardThatDoesNotExistInCardsXml", 3}, {"Phelddagrif", 2}, {"Giant Growth", 3}}, - {}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, EdgeCaseTesting) -{ - QString clipboard(R"( -// DeckName - - // Comment 1 - -// -//Comment [two] -//(test) Æ ’ | / (3) - - -// Mainboard (11 cards) -Æther Adept -2x Fire // Ice -1 Minsc & Boo, Timeless Heroes -3 Pain/Suffering -4X [B] Forest (3) - - -// Sideboard (11 cards) - -5x [WTH] Nature’s Resurgence -6X Gaea's Skyfolk -7 B.F.M. (Big Furry Monster) - - - -)"); - - Result result("DeckName", "Comment 1\n\nComment [two]\n(test) Æ ’ | / (3)", - {{"Aether Adept", 1}, - {"Fire // Ice", 2}, - {"Minsc & Boo, Timeless Heroes", 1}, - {"Pain // Suffering", 3}, - {"Forest", 4}}, - {{"Nature's Resurgence", 5}, {"Gaea's Skyfolk", 6}, {"B.F.M. (Big Furry Monster)", 7}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, CommentsBeforeCardsTesting) -{ - QString clipboard("// Title from website.com\n" - "// A nice deck\n" - "// With nice cards\n" - "\n" - "// Mainboard\n" - "1 test1\n" - "Sideboard\n" - "2 test2\n"); - - Result result("Title from website.com", "A nice deck\nWith nice cards", {{"test1", 1}}, {{"test2", 2}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, mainboardAsLine) -{ - QString clipboard("// Deck Name\n" - "\n" - "MainBoard: 3 cards\n" - "3 card\n" - "\n" - "SideBoard: 2 cards\n" - "2 sidecard\n"); - - Result result("Deck Name", "", {{"card", 3}}, {{"sidecard", 2}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, deckAsCard) -{ - QString clipboard("6 Deck of Cards But Animated\n" - "\n" - "7 Sideboard Card\n"); - - Result result("", "", {{"Deck of Cards But Animated", 6}}, {{"Sideboard Card", 7}}); - testDeck(clipboard, result); -} - -TEST(LoadingFromClipboardTest, emptyMainBoard) -{ - QString clipboard("deck\n" - "\n" - "sideboard\n"); - - testEmpty(clipboard); -} - -TEST(LoadingFromClipboardTest, emptyHash) -{ - QString clipboard(""); - - testHash(clipboard, "r8sq7riu"); -} - -TEST(LoadingFromClipboardTest, deckHash) -{ - QString clipboard("1 Mountain\n" - "2 Island\n" - "SB: 3 Forest\n"); - - testHash(clipboard, "5cac19qm"); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/movecard_tests/CMakeLists.txt b/tests/movecard_tests/CMakeLists.txt deleted file mode 100755 index 769047148..000000000 --- a/tests/movecard_tests/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_executable(reverse_card_move_test reverse_card_move_test.cpp) - -if(NOT GTEST_FOUND) - add_dependencies(reverse_card_move_test gtest) -endif() - -target_link_libraries( - reverse_card_move_test - PRIVATE libcockatrice_network_server_remote - PRIVATE libcockatrice_rng - PRIVATE Threads::Threads - PRIVATE ${GTEST_BOTH_LIBRARIES} - PRIVATE ${TEST_QT_MODULES} -) - -add_test(NAME reverse_card_move_test COMMAND reverse_card_move_test) diff --git a/tests/movecard_tests/reverse_card_move_test.cpp b/tests/movecard_tests/reverse_card_move_test.cpp deleted file mode 100644 index 2231a7e3b..000000000 --- a/tests/movecard_tests/reverse_card_move_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "game/server_abstract_player.h" -#include "game/server_card.h" -#include "game/server_cardzone.h" -#include "game/server_game.h" -#include "server_response_containers.h" -#include "server_room.h" -#include "server_test_helpers.h" - -#include -#include -#include -#include -#include - -RNG_Abstract *rng = nullptr; // this needs to be defined due to other functions in server - -TEST(ReverseCardMoveTest, MoveCardFromBottomTest) -{ - ServerInfo_User user; - user.set_name("test-user"); - - // instantiate a fake server instance - FakeServer server; - Server_Room room(0, 0, "", "", "", "", false, "", {}, &server); - Server_Game game(user, 1, "", "", 2, QList(), false, false, false, false, false, false, 20, false, &room); - Server_AbstractPlayer player(&game, 1, user, false, nullptr); - Server_CardZone deckZone(&player, ZoneNames::DECK, true, ServerInfo_Zone::PublicZone); - Server_CardZone exileZone(&player, ZoneNames::EXILE, true, ServerInfo_Zone::PublicZone); - - // setup the deck with 20 useless cards - for (int i = 0; i < 20; i++) { - auto *cardUseless = new Server_Card({"Card Useless", "card-Useless"}, player.newCardId(), i, 0); - deckZone.insertCard(cardUseless, i, 0); - } - - // add 4 cards to the end of it - auto *cardA = new Server_Card({"Card A", "card-a"}, player.newCardId(), 20, 0); - auto *cardB = new Server_Card({"Card B", "card-b"}, player.newCardId(), 21, 0); - auto *cardC = new Server_Card({"Card C", "card-c"}, player.newCardId(), 22, 0); - auto *cardD = new Server_Card({"Card D", "card-d"}, player.newCardId(), 23, 0); - - deckZone.insertCard(cardA, 20, 0); - deckZone.insertCard(cardB, 21, 0); - deckZone.insertCard(cardC, 22, 0); - deckZone.insertCard(cardD, 23, 0); - - // try to move them, with the expected client given order (n-3, n-2, n-1, n) - CardToMove moveA; - moveA.set_card_id(cardA->getId()); - CardToMove moveB; - moveB.set_card_id(cardB->getId()); - CardToMove moveC; - moveC.set_card_id(cardC->getId()); - CardToMove moveD; - moveD.set_card_id(cardD->getId()); - - QList cardsToMove = {&moveA, &moveB, &moveC, &moveD}; - GameEventStorage ges; - - const auto response = player.moveCard(ges, &deckZone, cardsToMove, &exileZone, 0, 0, false, false, false); - - EXPECT_EQ(response, Response::RespOk); - - int positionA; - int positionB; - int positionC; - int positionD; - // find the cards in the destination zone and check they are the right card - EXPECT_EQ(exileZone.getCard(cardA->getId(), &positionA), cardA); - EXPECT_EQ(exileZone.getCard(cardB->getId(), &positionB), cardB); - EXPECT_EQ(exileZone.getCard(cardC->getId(), &positionC), cardC); - EXPECT_EQ(exileZone.getCard(cardD->getId(), &positionD), cardD); - - // check that they are at the expected index - EXPECT_EQ(cardA->getX(), 3); - EXPECT_EQ(cardB->getX(), 2); - EXPECT_EQ(cardC->getX(), 1); - EXPECT_EQ(cardD->getX(), 0); - - // also check if the given positions are correct - EXPECT_EQ(positionA, 3); - EXPECT_EQ(positionB, 2); - EXPECT_EQ(positionC, 1); - EXPECT_EQ(positionD, 0); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/movecard_tests/server_test_helpers.h b/tests/movecard_tests/server_test_helpers.h deleted file mode 100644 index fd2ed6c17..000000000 --- a/tests/movecard_tests/server_test_helpers.h +++ /dev/null @@ -1,42 +0,0 @@ -#include "server.h" -#include "server_database_interface.h" - -class MockDatabaseInterface : public Server_DatabaseInterface -{ -public: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *, - const QString &, - const QString &, - const QString &, - QString &, - int &, - bool) override - { - return NotLoggedIn; - } - ServerInfo_User getUserData(const QString &, bool) override - { - return ServerInfo_User(); - } - int getNextGameId() override - { - return 1; - } - int getNextReplayId() override - { - return 1; - } - int getActiveUserCount(QString) override - { - return 1; - } -}; - -class FakeServer : public Server -{ -public: - FakeServer() - { - setDatabaseInterface(new MockDatabaseInterface()); - } -}; diff --git a/tests/oracle/CMakeLists.txt b/tests/oracle/CMakeLists.txt deleted file mode 100644 index c5c1e9097..000000000 --- a/tests/oracle/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -add_executable(parse_cipt_test ../../oracle/src/parsehelpers.cpp parse_cipt_test.cpp) - -if(NOT GTEST_FOUND) - add_dependencies(parse_cipt_test gtest) -endif() - -target_link_libraries(parse_cipt_test Threads::Threads ${GTEST_BOTH_LIBRARIES} ${TEST_QT_MODULES}) - -add_test(NAME parse_cipt_test COMMAND parse_cipt_test) diff --git a/tests/oracle/parse_cipt_test.cpp b/tests/oracle/parse_cipt_test.cpp deleted file mode 100644 index f5036f962..000000000 --- a/tests/oracle/parse_cipt_test.cpp +++ /dev/null @@ -1,219 +0,0 @@ -#include "../../oracle/src/parsehelpers.h" - -#include "gtest/gtest.h" - -TEST(ParseCiptTest, parsesThisEntersTapped) -{ - auto name = "Boring Fields"; - auto text = "This land enters tapped."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesThisEntersTheBattlefieldTapped) -{ - auto name = "Boring Fields"; - auto text = "This land enters the battlefield tapped.\n" - "{T}: Add {G}."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesItEntersTappedAtEndOfSentence) -{ - auto name = "Shocking Fields"; - auto text = "As this land enters, you may pay 2 life. If you don't, it enters tapped.\n" - "{T}: Add {G}."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesThisEntersTappedWhenNotOnFirstLine) -{ - auto name = "Boring Fields"; - auto text = "Flying\n" - "This land enters tapped.\n" - "{T}: Add {G}."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesFullNameWithUnderscoreAppendedText) -{ - auto name = "Boring Fields_SL50"; - auto text = "Boring Fields enters tapped.\n" - "{T}: Add {G}."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesFullNameWithBracketsAppendedText) -{ - auto name = "Boring Fields (SL50)"; - auto text = "Boring Fields enters tapped.\n" - "{T}: Add {G}."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesFullNameWithComma) -{ - auto name = "Bob, the Legend"; - auto text = "Bob, the Legend enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesFullNameWithCommaAtEndOfSentence) -{ - auto name = "Bob, the Legend"; - auto text = "As Bob, the Legend enters, you may pay 2 life. If you don't, Bob, the Legend enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesFullNameWithApostropheAtEndOfSentence) -{ - auto name = "Bob's Bobber"; - auto text = "As Bob's Bobber enters, you may pay 2 life. If you don't, Bob's Bobber enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesShortnameEndingWithComma) -{ - auto name = "Bob, the Legend"; - auto text = "Bob enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesShortnameEndingWithSpace) -{ - auto name = "Bob the Legend"; - auto text = "Bob enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithComma) -{ - auto name = "Bob Dod, the Legend"; - auto text = "Bob Dod enters tapped.\n" - "Whenever Bob Dod attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpace) -{ - auto name = "Bob Dod the Legend"; - auto text = "Bob Dod enters tapped.\n" - "Whenever Bob Dod attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesShortnameEndingWithSpaceWithUnderscoreAppendedText) -{ - auto name = "Bob the Legend_SL50"; - auto text = "Bob enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesShortnameEndingWithSpaceWithBracketsAppendedText) -{ - auto name = "Bob the Legend (SL50)"; - auto text = "Bob enters tapped.\n" - "Whenever Bob attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpaceWithUnderscoreAppendedText) -{ - auto name = "Bob Dod the Legend_SL50"; - auto text = "Bob Dod enters tapped.\n" - "Whenever Bob Dod attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesMultiWordShortnameEndingWithSpaceWithBracketsAppendedText) -{ - auto name = "Bob Dod the Legend (SL50)"; - auto text = "Bob Dod enters tapped.\n" - "Whenever Bob Dod attacks, you win the game."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsEmptyText) -{ - auto name = "Vanilla Dude"; - auto text = ""; - - ASSERT_FALSE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsEntersTappedUnless) -{ - auto name = "Fast Fields"; - auto text = "This land enters tapped unless you control another land."; - - ASSERT_FALSE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsWhenNameIsDifferent) -{ - auto name = "Boring Fields"; - auto text = "Fast Fields enters tapped."; - - ASSERT_FALSE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsOtherCreaturesEnterTapped) -{ - auto name = "Imposing Guy"; - auto text = "Other creatures enter tapped."; - - ASSERT_FALSE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsAbilityGrantingEntersTapped) -{ - auto name = "Imposing Guy"; - auto text = "Other creatures have \"This creature enters tapped\"."; - - ASSERT_FALSE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, parsesEntersTappedAndAbilityGrantingEntersTappedOnSameCard) -{ - auto name = "Imposing Guy"; - auto text = "This creature enters tapped." - "Other creatures have \"This creature enters tapped\"."; - - ASSERT_TRUE(parseCipt(name, text)); -} - -TEST(ParseCiptTest, rejectsItEntersTappedAndAttacking) -{ - auto name = "Token Maker"; - auto text = "When Token Maker attacks, create a token. It enters tapped and attacking."; - - ASSERT_FALSE(parseCipt(name, text)); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/password_hash_test.cpp b/tests/password_hash_test.cpp deleted file mode 100644 index 38d9b6315..000000000 --- a/tests/password_hash_test.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "gtest/gtest.h" -#include -#include -#include - -RNG_Abstract *rng; - -namespace -{ -class PasswordHashTest : public ::testing::Test -{ -protected: - void SetUp() override - { - rng = new RNG_SFMT; - } - - void TearDown() override - { - delete rng; - } -}; - -TEST(PasswordHashTest, RegressionTest) -{ - QString salt = "saltsaltsaltsalt"; - QString password = "password"; - QString expected = "vmKoWv975yf+WT2QCXhW48JNzZ2ghGxdgNvuKLBU0h7s6AQHSG72J6QO4ZswuSeqvBbAXbmgJSRBaSJrgc55WA=="; - QString hash = PasswordHasher::computeHash(password, salt); - ASSERT_EQ(hash, salt + expected) << "The computed hash value remains the same"; -} -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/test_age_formatting.cpp b/tests/test_age_formatting.cpp deleted file mode 100644 index e4fc64cf9..000000000 --- a/tests/test_age_formatting.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "../cockatrice/src/interface/widgets/server/user/user_info_box.h" - -#include "gtest/gtest.h" - -namespace -{ -using dayyear = QPair; - -TEST(AgeFormatting, Zero) -{ - auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2000, 1, 1)); - ASSERT_EQ(got, dayyear(0, 0)) << "these are the same day"; -} - -TEST(AgeFormatting, LeapDay) -{ - auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2000, 3, 1)); - ASSERT_EQ(got, dayyear(2, 0)) << "there is a leap day in between these days"; -} - -TEST(AgeFormatting, LeapYear) -{ - auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 1, 1), QDate(2001, 1, 1)); - ASSERT_EQ(got, dayyear(0, 1)) << "there is a leap day in between these dates, but that's fine"; -} - -TEST(AgeFormatting, LeapDayWithYear) -{ - auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2000, 2, 28), QDate(2001, 3, 1)); - ASSERT_EQ(got, dayyear(1, 1)) << "there is a leap day in between these days but not in the last year"; -} - -TEST(AgeFormatting, LeapDayThisYear) -{ - auto got = UserInfoBox::getDaysAndYearsBetween(QDate(2003, 2, 28), QDate(2004, 3, 1)); - ASSERT_EQ(got, dayyear(2, 1)) << "there is a leap day in between these days this year"; -} -} // namespace - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/themes/CMakeLists.txt b/themes/CMakeLists.txt new file mode 100644 index 000000000..dc3b9b50c --- /dev/null +++ b/themes/CMakeLists.txt @@ -0,0 +1,22 @@ +# CMakeLists for themes directory +# +# add themes subfolders + +SET(defthemes + Default + Fabric + Leather + Plasma + VelvetMarble +) + +if(UNIX) + if(APPLE) + INSTALL(DIRECTORY ${defthemes} DESTINATION cockatrice.app/Contents/Resources/themes/) + else() + # Assume linux + INSTALL(DIRECTORY ${defthemes} DESTINATION share/cockatrice/themes/) + endif() +elseif(WIN32) + INSTALL(DIRECTORY ${defthemes} DESTINATION themes/) +endif() diff --git a/servatrice/mysql-storage/.gitignore b/themes/Default/.gitignore similarity index 100% rename from servatrice/mysql-storage/.gitignore rename to themes/Default/.gitignore diff --git a/cockatrice/themes/Fabric/zones/handzone.png b/themes/Fabric/zones/handzone.png similarity index 100% rename from cockatrice/themes/Fabric/zones/handzone.png rename to themes/Fabric/zones/handzone.png diff --git a/cockatrice/themes/Fabric/zones/playerzone.png b/themes/Fabric/zones/playerzone.png similarity index 100% rename from cockatrice/themes/Fabric/zones/playerzone.png rename to themes/Fabric/zones/playerzone.png diff --git a/cockatrice/themes/Fabric/zones/stackzone.png b/themes/Fabric/zones/stackzone.png similarity index 100% rename from cockatrice/themes/Fabric/zones/stackzone.png rename to themes/Fabric/zones/stackzone.png diff --git a/cockatrice/themes/Fabric/zones/tablezone.png b/themes/Fabric/zones/tablezone.png similarity index 100% rename from cockatrice/themes/Fabric/zones/tablezone.png rename to themes/Fabric/zones/tablezone.png diff --git a/cockatrice/themes/Leather/zones/handzone.png b/themes/Leather/zones/handzone.png similarity index 100% rename from cockatrice/themes/Leather/zones/handzone.png rename to themes/Leather/zones/handzone.png diff --git a/cockatrice/themes/Leather/zones/playerzone.png b/themes/Leather/zones/playerzone.png similarity index 100% rename from cockatrice/themes/Leather/zones/playerzone.png rename to themes/Leather/zones/playerzone.png diff --git a/cockatrice/themes/Leather/zones/stackzone.png b/themes/Leather/zones/stackzone.png similarity index 100% rename from cockatrice/themes/Leather/zones/stackzone.png rename to themes/Leather/zones/stackzone.png diff --git a/cockatrice/themes/Leather/zones/tablezone.png b/themes/Leather/zones/tablezone.png similarity index 100% rename from cockatrice/themes/Leather/zones/tablezone.png rename to themes/Leather/zones/tablezone.png diff --git a/cockatrice/themes/Plasma/zones/handzone.png b/themes/Plasma/zones/handzone.png similarity index 100% rename from cockatrice/themes/Plasma/zones/handzone.png rename to themes/Plasma/zones/handzone.png diff --git a/cockatrice/themes/Plasma/zones/playerzone.png b/themes/Plasma/zones/playerzone.png similarity index 100% rename from cockatrice/themes/Plasma/zones/playerzone.png rename to themes/Plasma/zones/playerzone.png diff --git a/cockatrice/themes/Plasma/zones/stackzone.png b/themes/Plasma/zones/stackzone.png similarity index 100% rename from cockatrice/themes/Plasma/zones/stackzone.png rename to themes/Plasma/zones/stackzone.png diff --git a/cockatrice/themes/Plasma/zones/tablezone.png b/themes/Plasma/zones/tablezone.png similarity index 100% rename from cockatrice/themes/Plasma/zones/tablezone.png rename to themes/Plasma/zones/tablezone.png diff --git a/cockatrice/themes/VelvetMarble/zones/handzone.jpg b/themes/VelvetMarble/zones/handzone.jpg similarity index 100% rename from cockatrice/themes/VelvetMarble/zones/handzone.jpg rename to themes/VelvetMarble/zones/handzone.jpg diff --git a/cockatrice/themes/VelvetMarble/zones/playerzone.jpg b/themes/VelvetMarble/zones/playerzone.jpg similarity index 100% rename from cockatrice/themes/VelvetMarble/zones/playerzone.jpg rename to themes/VelvetMarble/zones/playerzone.jpg diff --git a/cockatrice/themes/VelvetMarble/zones/stackzone.jpg b/themes/VelvetMarble/zones/stackzone.jpg similarity index 100% rename from cockatrice/themes/VelvetMarble/zones/stackzone.jpg rename to themes/VelvetMarble/zones/stackzone.jpg diff --git a/cockatrice/themes/VelvetMarble/zones/tablezone.jpg b/themes/VelvetMarble/zones/tablezone.jpg similarity index 100% rename from cockatrice/themes/VelvetMarble/zones/tablezone.jpg rename to themes/VelvetMarble/zones/tablezone.jpg diff --git a/travis-compile.sh b/travis-compile.sh new file mode 100755 index 000000000..0d26c5e4b --- /dev/null +++ b/travis-compile.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +./servatrice/check_schema_version.sh + +mkdir build +cd build +prefix="" +if [[ $TRAVIS_OS_NAME == "osx" && $QT4 == 0 ]]; then + prefix="-DCMAKE_PREFIX_PATH=$(echo /usr/local/Cellar/qt5/5.*/)" +fi +if [[ $TRAVIS_OS_NAME == "linux" && $QT4 == 0 ]]; then + prefix="-DCMAKE_PREFIX_PATH=$(echo /opt/qt5*/lib/cmake/)" + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(echo /opt/qt5*/lib/)" +fi + +if [[ $BUILDTYPE == "Debug" ]]; then + cmake .. -DWITH_SERVER=1 -DTEST=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix + make -j2 + make test +else + cmake .. -DWITH_SERVER=1 -DCMAKE_BUILD_TYPE=$BUILDTYPE -DWITH_QT4=$QT4 $prefix + make package -j2 +fi diff --git a/travis-dependencies.sh b/travis-dependencies.sh new file mode 100755 index 000000000..8a64dd3c3 --- /dev/null +++ b/travis-dependencies.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +if [[ $TRAVIS_OS_NAME == "osx" ]] ; then + brew update > /dev/null + if (( QT4 )); then + brew install qt protobuf libgcrypt > /dev/null + else + brew install qt5 protobuf libgcrypt > /dev/null + fi + brew unlink cmake + brew upgrade cmake + +else + + # common prerequisites + sudo apt-get update -qq + sudo apt-get install -y libprotobuf-dev protobuf-compiler cmake bc + + if (( QT4 )); then + # qt4 prerequisites + sudo apt-get install -y qtmobility-dev libqt4-dev + else + # qt5 prerequisites + sudo apt-get install -y libprotobuf-dev protobuf-compiler \ + qt5-default qttools5-dev qttools5-dev-tools \ + qtmultimedia5-dev libqt5multimedia5-plugins libqt5svg5-dev libqt5sql5-mysql + fi + + # prerequisites for tests + if [[ $BUILDTYPE == "Debug" ]]; then + if [[ $DIST == "precise" ]]; then + sudo add-apt-repository -y ppa:george-edison55/precise-backports + sudo apt-get update -qq + sudo apt-get install -y cmake cmake-data libgtest-dev + else + sudo add-apt-repository -y ppa:george-edison55/cmake-3.x + sudo apt-get update -qq + sudo apt-get install -y cmake cmake-extras libgtest-dev + fi + + sudo mkdir /usr/src/gtest/build + cd /usr/src/gtest/build + sudo cmake .. -DBUILD_SHARED_LIBS=1 + sudo make -j2 + sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so + sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so + cd - + fi +fi diff --git a/vcpkg b/vcpkg deleted file mode 160000 index 74e653621..000000000 --- a/vcpkg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 74e6536215718009aae747d86d84b78376bf9e09 diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index ac7b75d07..000000000 --- a/vcpkg.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", - "dependencies": [ - "gtest", - "liblzma", - "openssl", - "protobuf", - "pthreads", - "zlib" - ] -} diff --git a/webclient/.env b/webclient/.env deleted file mode 100644 index 8592b28b4..000000000 --- a/webclient/.env +++ /dev/null @@ -1 +0,0 @@ -# Future template for server admin configuration diff --git a/webclient/.env.development b/webclient/.env.development deleted file mode 100644 index 2accbba81..000000000 --- a/webclient/.env.development +++ /dev/null @@ -1 +0,0 @@ -ESLINT_NO_DEV_ERRORS=true diff --git a/webclient/.env.production b/webclient/.env.production deleted file mode 100644 index 02269f00d..000000000 --- a/webclient/.env.production +++ /dev/null @@ -1 +0,0 @@ -DISABLE_ESLINT_PLUGIN=true diff --git a/webclient/.env.test b/webclient/.env.test deleted file mode 100644 index 8711f95ab..000000000 --- a/webclient/.env.test +++ /dev/null @@ -1 +0,0 @@ -CI=true diff --git a/webclient/.eslintrc.js b/webclient/.eslintrc.js deleted file mode 100644 index 98ce44430..000000000 --- a/webclient/.eslintrc.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": {"project": ["./tsconfig.json"]}, - "plugins": [ - "@typescript-eslint" - ], - "ignorePatterns": ["node_modules/*", "build/*", "public/pb/*"], - "env": { - "jest": true - }, - "rules": { - "array-bracket-spacing": ["error", "never"], - "arrow-spacing": ["error", {"before": true, "after": true}], - "block-spacing": ["error", "always"], - "brace-style": ["error", "1tbs", {"allowSingleLine": false}], - "comma-spacing": ["error", {"before": false, "after": true}], - "comma-style": ["error", "last"], - "computed-property-spacing": ["error", "never"], - "curly": ["error", "all"], - "dot-location": ["error", "property"], - "eol-last": ["error"], - "func-names": ["warn"], - "indent": ["error", 2, {"SwitchCase": 1}], - "key-spacing": ["error", {"beforeColon": false, "afterColon": true}], - "keyword-spacing": ["error"], - "linebreak-style": ["error", (process.platform === "win32" ? "windows" : "unix")], - "max-len": ["error", {"code": 140}], - "no-eq-null": ["off"], - "no-func-assign": ["error"], - "no-inline-comments": ["error"], - "no-mixed-spaces-and-tabs": ["error"], - "no-multi-spaces": ["error"], - "no-spaced-func": ["error"], - "no-trailing-spaces": ["error"], - "no-var": ["error"], - "object-curly-spacing": ["error", "always"], - "one-var": ["error", "never"], - "one-var-declaration-per-line": ["error"], - "quotes": ["error", "single"], - "semi-spacing": ["error", {"before": false, "after": true}], - "space-before-blocks": ["error"], - "space-before-function-paren": ["error", {"asyncArrow": "always", "anonymous": "never", "named": "never"}], - "space-in-parens": ["error", "never"], - "space-infix-ops": ["error"], - "space-unary-ops": ["error", {"words": true, "nonwords": false}] - } -} \ No newline at end of file diff --git a/webclient/.gitignore b/webclient/.gitignore deleted file mode 100644 index 2b30e2c8d..000000000 --- a/webclient/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# generated ./src files -/src/proto-files.json -/src/server-props.json - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/webclient/.npmrc b/webclient/.npmrc deleted file mode 100644 index 521a9f7c0..000000000 --- a/webclient/.npmrc +++ /dev/null @@ -1 +0,0 @@ -legacy-peer-deps=true diff --git a/webclient/README.md b/webclient/README.md deleted file mode 100644 index 436ab4fad..000000000 --- a/webclient/README.md +++ /dev/null @@ -1,73 +0,0 @@ -## Application Architecture -![Application Architecture](architecture.png?raw=true "Application Architecture") - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
-You will also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.
-See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.
-It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -## To-Do List - -1) RefreshGuard modal - - there is no browser support for displaying custom output to window.onbeforeunload - - we should also display a custom modal explaining why they shouldnt refresh or navigate from the site - - ideally, the custom popup can be synced with the alert, so when the alert is closed, the modal closes too - -2) Disable AutoScrollToBottom when the user has scrolled up - - when the user scrolls back to bottom, it should renable - - renable after a period of inactivity (3 minutes?) - -3) Figure out how to type components w/ RouteComponentProps - - Component> - -4) clear input onSubmit - -5) figure out how to reflect server status changes in the ui - -6) Account page - -7) Register/Reset Password forms - -8) Message User - -9) Main Nav scheme diff --git a/webclient/architecture.png b/webclient/architecture.png deleted file mode 100644 index 0226eb201..000000000 Binary files a/webclient/architecture.png and /dev/null differ diff --git a/webclient/package-lock.json b/webclient/package-lock.json deleted file mode 100644 index 6b12b4ad6..000000000 --- a/webclient/package-lock.json +++ /dev/null @@ -1,34786 +0,0 @@ -{ - "name": "webclient", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "webclient", - "version": "1.0.0", - "dependencies": { - "@emotion/react": "^11.8.2", - "@emotion/styled": "^11.8.1", - "@mui/icons-material": "^5.5.1", - "@mui/material": "^5.5.1", - "crypto-js": "^4.2.0", - "dexie": "^3.2.2", - "final-form": "^4.20.6", - "final-form-set-field-touched": "^1.0.1", - "i18next": "^22.0.4", - "i18next-browser-languagedetector": "^7.0.0", - "i18next-icu": "^2.0.3", - "intl-messageformat": "^10.2.1", - "lodash": "^4.17.21", - "prop-types": "^15.8.1", - "protobufjs": "^7.2.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-final-form": "^6.5.8", - "react-final-form-listeners": "^1.0.3", - "react-i18next": "^12.0.0", - "react-redux": "^8.0.4", - "react-router-dom": "^6.2.2", - "react-scripts": "5.0.1", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.6", - "redux": "^4.1.2", - "redux-form": "^8.3.8", - "redux-thunk": "^2.4.1", - "rxjs": "^7.5.4", - "sanitize-html": "^2.7.3" - }, - "devDependencies": { - "@babel/core": "^7.17.5", - "@mui/types": "^7.1.3", - "@testing-library/jest-dom": "^5.16.2", - "@testing-library/react": "^13.4.0", - "@types/jest": "29.2.0", - "@types/jquery": "^3.5.14", - "@types/lodash": "^4.14.179", - "@types/node": "18.11.7", - "@types/prop-types": "^15.7.4", - "@types/react": "18.0.24", - "@types/react-dom": "18.0.8", - "@types/react-redux": "^7.1.23", - "@types/react-router-dom": "^5.3.3", - "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/react-window": "^1.8.5", - "@types/redux-form": "^8.3.3", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", - "fs-extra": "^10.0.1", - "husky": "^8.0.1", - "typescript": "^4.6.2" - } - }, - "node_modules/@adobe/css-tools": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", - "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", - "dev": true - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@apideck/better-ajv-errors": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", - "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", - "dependencies": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "ajv": ">=8" - } - }, - "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==", - "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.20.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.0.tgz", - "integrity": "sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", - "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helpers": "^7.19.4", - "@babel/parser": "^7.19.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", - "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || >=14.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.11.0", - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", - "dependencies": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", - "dependencies": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", - "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", - "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", - "dependencies": { - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", - "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.19.4", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", - "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", - "dependencies": { - "@babel/types": "^7.19.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "dependencies": { - "@babel/types": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "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==", - "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==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", - "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", - "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz", - "integrity": "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.0.tgz", - "integrity": "sha512-vnuRRS20ygSxclEYikHzVrP9nZDFXaSzvJxGLQNAiBX041TmhS4hOUHWNIpq/q4muENuEP9XPJFXTNFejhemkg==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", - "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", - "dependencies": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.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==", - "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==", - "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==", - "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==", - "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-decorators": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", - "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "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==", - "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==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "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==", - "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==", - "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==", - "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==", - "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==", - "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==", - "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==", - "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==", - "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.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", - "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", - "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", - "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", - "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-flow": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", - "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", - "dependencies": { - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", - "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-simple-access": "^7.19.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", - "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.19.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", - "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", - "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.12.tgz", - "integrity": "sha512-Q99U9/ttiu+LMnRU8psd23HhvwXmKWDQIpocm0JKaICcZHnw+mdQbHm6xnSy7dOl8I5PELakYtNBubNQlBXbZw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", - "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.19.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", - "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", - "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz", - "integrity": "sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.20.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", - "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", - "dependencies": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.19.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.19.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", - "@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", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.19.4", - "@babel/plugin-transform-classes": "^7.19.0", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.19.4", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.0", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.19.4", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", - "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.1.tgz", - "integrity": "sha512-909rVuj3phpjW6y0MCXAZ5iNeORePa6ldJvp2baWGcTjwqbBDDz6xoS5JHJ7lS88NlwLYj07ImL/8IUMtDZzTA==", - "dependencies": { - "core-js-pure": "^3.30.2" - }, - "engines": { - "node": ">=6.9.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==", - "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.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", - "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==" - }, - "node_modules/@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" - }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2", - "postcss-selector-parser": "^6.0.10" - } - }, - "node_modules/@emotion/babel-plugin": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", - "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.1", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.1.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", - "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", - "dependencies": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.1", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.1.3" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", - "dependencies": { - "@emotion/memoize": "^0.8.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" - }, - "node_modules/@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", - "dependencies": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", - "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" - }, - "node_modules/@emotion/styled": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.5.tgz", - "integrity": "sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/is-prop-valid": "^1.2.0", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@emotion/react": "^11.0.0-rc.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" - }, - "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@formatjs/ecma402-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.13.0.tgz", - "integrity": "sha512-CQ8Ykd51jYD1n05dtoX6ns6B9n/+6ZAxnWUAonvHC4kkuAemROYBhHkEB4tm1uVrRlE7gLDqXkAnY51Y0pRCWQ==", - "dependencies": { - "@formatjs/intl-localematcher": "0.2.31", - "tslib": "2.4.0" - } - }, - "node_modules/@formatjs/fast-memoize": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.6.tgz", - "integrity": "sha512-9CWZ3+wCkClKHX+i5j+NyoBVqGf0pIskTo6Xl6ihGokYM2yqSSS68JIgeo+99UIHc+7vi9L3/SDSz/dWI9SNlA==", - "dependencies": { - "tslib": "2.4.0" - } - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.10.tgz", - "integrity": "sha512-KkRMxhifWkRC45dhM9tqm0GXbb6NPYTGVYY3xx891IKc6p++DQrZTnmkVSNNO47OEERLfuP2KkPFPJBuu8z/wg==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.13.0", - "@formatjs/icu-skeleton-parser": "1.3.14", - "tslib": "2.4.0" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.14.tgz", - "integrity": "sha512-7bv60HQQcBb3+TSj+45tOb/CHV5z1hOpwdtS50jsSBXfB+YpGhnoRsZxSRksXeCxMy6xn6tA6VY2601BrrK+OA==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.13.0", - "tslib": "2.4.0" - } - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.2.31", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz", - "integrity": "sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA==", - "dependencies": { - "tslib": "2.4.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" - }, - "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==", - "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/load-nyc-config/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/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==", - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/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==" - }, - "node_modules/@jest/console/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/console/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/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==" - }, - "node_modules/@jest/core/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/core/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/environment/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/environment/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/environment/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/environment/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/environment/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==" - }, - "node_modules/@jest/environment/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/environment/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.2.2.tgz", - "integrity": "sha512-vwnVmrVhTmGgQzyvcpze08br91OL61t9O0lJMDyb6Y/D8EKQ9V7rGUb/p7PDt0GPzK0zFYqXWFo4EO2legXmkg==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.2.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/fake-timers/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/fake-timers/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/fake-timers/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==" - }, - "node_modules/@jest/fake-timers/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/fake-timers/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/globals/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/globals/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/globals/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/globals/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==" - }, - "node_modules/@jest/globals/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/globals/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/globals/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/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==" - }, - "node_modules/@jest/reporters/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/reporters/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/reporters/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/source-map/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/test-result/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/test-result/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/test-result/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/test-result/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==" - }, - "node_modules/@jest/test-result/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-result/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/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==" - }, - "node_modules/@jest/transform/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/@jest/transform/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jest/transform/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.2.1.tgz", - "integrity": "sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "@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/@jest/types/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/@jest/types/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/@jest/types/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/@jest/types/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/@jest/types/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/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" - }, - "node_modules/@mui/base": { - "version": "5.0.0-alpha.103", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.103.tgz", - "integrity": "sha512-fJIyB2df3CHn7D26WHnutnY7vew6aytTlhmRJz6GX7ag19zU2GcOUhJAzY5qwWcrXKnlYgzimhEjaEnuiUWU4g==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@emotion/is-prop-valid": "^1.2.0", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "@popperjs/core": "^2.11.6", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/core-downloads-tracker": { - "version": "5.10.11", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.11.tgz", - "integrity": "sha512-u5ff+UCFDHcR8MoQ8tuJR4c35vt7T/ki3aMEE2O3XQoGs8KJSrBiisFpFKyldg9/W2NSyoZxN+kxEGIfRxh+9Q==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - } - }, - "node_modules/@mui/icons-material": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.10.9.tgz", - "integrity": "sha512-sqClXdEM39WKQJOQ0ZCPTptaZgqwibhj2EFV9N0v7BU1PO8y4OcX/a2wIQHn4fNuDjIZktJIBrmU23h7aqlGgg==", - "dependencies": { - "@babel/runtime": "^7.19.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/material": { - "version": "5.10.11", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.11.tgz", - "integrity": "sha512-KJ0wPCTbv6sFzwA3dgg0gowdfF+SRl7D510J9l6Nl/KFX0EawcewQudqKY4slYGFXniKa5PykqokpaWXsCCPqg==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@mui/base": "5.0.0-alpha.103", - "@mui/core-downloads-tracker": "^5.10.11", - "@mui/system": "^5.10.10", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "@types/react-transition-group": "^4.4.5", - "clsx": "^1.2.1", - "csstype": "^3.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/private-theming": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.10.9.tgz", - "integrity": "sha512-BN7/CnsVPVyBaQpDTij4uV2xGYHHHhOgpdxeYLlIu+TqnsVM7wUeF+37kXvHovxM6xmL5qoaVUD98gDC0IZnHg==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@mui/utils": "^5.10.9", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/styled-engine": { - "version": "5.10.8", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.8.tgz", - "integrity": "sha512-w+y8WI18EJV6zM/q41ug19cE70JTeO6sWFsQ7tgePQFpy6ToCVPh0YLrtqxUZXSoMStW5FMw0t9fHTFAqPbngw==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@emotion/cache": "^11.10.3", - "csstype": "^3.1.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@emotion/react": "^11.4.1", - "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - } - }, - "node_modules/@mui/system": { - "version": "5.10.10", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.10.tgz", - "integrity": "sha512-TXwtKN0adKpBrZmO+eilQWoPf2veh050HLYrN78Kps9OhlvO70v/2Kya0+mORFhu9yhpAwjHXO8JII/R4a5ZLA==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@mui/private-theming": "^5.10.9", - "@mui/styled-engine": "^5.10.8", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "clsx": "^1.2.1", - "csstype": "^3.1.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.0.tgz", - "integrity": "sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA==", - "peerDependencies": { - "@types/react": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@mui/utils": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.9.tgz", - "integrity": "sha512-2tdHWrq3+WCy+G6TIIaFx3cg7PorXZ71P375ExuX61od1NOAJP1mK90VxQ8N4aqnj2vmO3AQDkV4oV2Ktvt4bA==", - "dependencies": { - "@babel/runtime": "^7.19.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^16.7.1 || ^17.0.0", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dependencies": { - "eslint-scope": "5.1.1" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.8.tgz", - "integrity": "sha512-wxXRwf+IQ6zvHSJZ+5T2RQNEsq+kx4jKRXfFvdt3nBIUzJUAvXEFsUeoaohDe/Kr84MTjGwcuIUPNcstNJORsA==", - "dependencies": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <4.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "node_modules/@remix-run/router": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", - "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", - "dependencies": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "@types/babel__core": "^7.1.9", - "rollup": "^1.20.0||^2.0.0" - }, - "peerDependenciesMeta": { - "@types/babel__core": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "engines": { - "node": ">= 8.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" - } - }, - "node_modules/@rollup/pluginutils/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "dependencies": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "dependencies": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "dependencies": { - "@babel/types": "^7.12.6" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "dependencies": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/@testing-library/dom": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz", - "integrity": "sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/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/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", - "dev": true, - "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/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/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/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/@testing-library/jest-dom/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/@testing-library/jest-dom/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/@testing-library/jest-dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", - "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", - "dependencies": { - "@babel/types": "^7.3.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.4.9", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", - "integrity": "sha512-jFCSo4wJzlHQLCpceUhUnXdrPuCNOjGFMQ8Eg6JXxlz3QaCKOb7eGi2cephQdM4XTYsNej69P9JDJ1zqNIbncQ==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" - }, - "node_modules/@types/express": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", - "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", - "dev": true - }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.0.tgz", - "integrity": "sha512-KO7bPV21d65PKwv3LLsD8Jn3E05pjNjRZvkm+YTacWhVmykAb07wW6IkZUmQAltwQafNcDUEUrMO2h3jeBSisg==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/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/@types/jest/node_modules/pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@types/jquery": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", - "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", - "dev": true, - "dependencies": { - "@types/sizzle": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "node_modules/@types/lodash": { - "version": "4.14.186", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", - "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" - }, - "node_modules/@types/node": { - "version": "18.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", - "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==" - }, - "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "node_modules/@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "node_modules/@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" - }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "node_modules/@types/react": { - "version": "18.0.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", - "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.0.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.8.tgz", - "integrity": "sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-is": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", - "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-redux": { - "version": "7.1.24", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", - "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", - "dev": true, - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.19", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.19.tgz", - "integrity": "sha512-Fv/5kb2STAEMT3wHzdKQK2z8xKq38EDIGVrutYLmQVVLe+4orDFquU52hQrULnEHinMKv9FSA6lf9+uNT1ITtA==", - "dev": true, - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "dev": true, - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-virtualized-auto-sizer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz", - "integrity": "sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-window": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz", - "integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/redux-form": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@types/redux-form/-/redux-form-8.3.5.tgz", - "integrity": "sha512-SchB4i7nxgWNbJS4cXEZducztkvHzVrb5xlAXwfLpbrLPo6tMY06+kx1GqMv42+YnGy9TpCAkF51a21HatqWBA==", - "dev": true, - "dependencies": { - "@types/react": "*", - "redux": "^3.6.0 || ^4.0.0" - } - }, - "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "node_modules/@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" - }, - "node_modules/@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", - "dependencies": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "node_modules/@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" - }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", - "dev": true, - "dependencies": { - "@types/jest": "*" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, - "node_modules/@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", - "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", - "dependencies": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/type-utils": "5.41.0", - "@typescript-eslint/utils": "5.41.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.41.0.tgz", - "integrity": "sha512-/qxT2Kd2q/A22JVIllvws4rvc00/3AT4rAo/0YgEN28y+HPhbJbk6X4+MAHEoZzpNyAOugIT7D/OLnKBW8FfhA==", - "dependencies": { - "@typescript-eslint/utils": "5.41.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", - "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", - "dependencies": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", - "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", - "dependencies": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", - "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", - "dependencies": { - "@typescript-eslint/typescript-estree": "5.41.0", - "@typescript-eslint/utils": "5.41.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", - "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", - "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", - "dependencies": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", - "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", - "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", - "dependencies": { - "@typescript-eslint/types": "5.41.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-node/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", - "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "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==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.2.tgz", - "integrity": "sha512-JWydkr9MirMg2jGJstDqDgzoHqaFbv7n1ghfXYdtEgXWgdq3jz7IU3SQvtj9k3mAszQBiTpQhFdlH+JIRuGTzg==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "node_modules/array-includes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", - "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" - }, - "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "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/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.0.tgz", - "integrity": "sha512-4+rr8eQ7+XXS5nZrKcMO/AikHL0hVqy+lHWAnE3xdHl+aguag8SOQ6eEqLexwLNWgXIMfunGuD3ON1/6Kyet0A==", - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" - }, - "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-jest/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/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==" - }, - "node_modules/babel-jest/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", - "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "engines": { - "node": ">= 8.9" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-loader/node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "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==", - "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-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, - "node_modules/babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "peerDependencies": { - "@babel/core": "^7.1.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "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==" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, - "node_modules/bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", - "dependencies": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/bonjour-service": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", - "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", - "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "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==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "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.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "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==", - "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==" - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dependencies": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", - "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/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "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==", - "engines": { - "node": ">=10" - } - }, - "node_modules/check-types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", - "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" - }, - "node_modules/clean-css": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", - "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dependencies": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" - }, - "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/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "node_modules/core-js": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", - "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", - "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", - "dependencies": { - "browserslist": "^4.21.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", - "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-declaration-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.7", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "dependencies": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@parcel/css": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/css-minimizer-webpack-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "node_modules/css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dependencies": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true - }, - "node_modules/cssdb": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.2.tgz", - "integrity": "sha512-Vm4b6P/PifADu0a76H0DKRNVWq3Rq9xa/Nx6oEMUBJlwTUuZoZ3dkZxo8Gob3UEL53Cq+Ma1GBgISed6XEBs3w==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssnano": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", - "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", - "dependencies": { - "cssnano-preset-default": "^5.2.13", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/cssnano" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-preset-default": { - "version": "5.2.13", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", - "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.0", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.3", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.1", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dependencies": { - "css-tree": "^1.1.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/csso/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/csso/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - }, - "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" - }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" - }, - "node_modules/deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", - "isarray": "^2.0.5", - "object-is": "^1.1.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==", - "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==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "node_modules/detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dependencies": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "bin": { - "detect": "bin/detect-port", - "detect-port": "bin/detect-port" - }, - "engines": { - "node": ">= 4.2.1" - } - }, - "node_modules/detect-port-alt/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/detect-port-alt/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "dependencies": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "detective": "bin/detective.js" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/dexie": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.2.tgz", - "integrity": "sha512-q5dC3HPmir2DERlX+toCBbHQXW5MsyrFqPFcovkH9N2S/UW/H3H5AWAB6iEOExeraAu+j+zRDG+zg/D7YhH0qg==", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "node_modules/diff-sequences": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.2.0.tgz", - "integrity": "sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, - "node_modules/dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", - "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", - "dev": true - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" - }, - "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/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==", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==" - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", - "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "dependencies": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "dependencies": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@babel/plugin-syntax-flow": "^7.14.5", - "@babel/plugin-transform-react-jsx": "^7.14.9", - "eslint": "^8.1.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dependencies": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "dependencies": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "dependencies": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", - "dependencies": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-testing-library": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", - "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", - "dependencies": { - "@typescript-eslint/utils": "^5.13.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0", - "npm": ">=6" - }, - "peerDependencies": { - "eslint": "^7.5.0 || ^8.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", - "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/eslint-webpack-plugin/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/eslint-webpack-plugin/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "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==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.2.2.tgz", - "integrity": "sha512-hE09QerxZ5wXiOhqkXy5d2G9ar+EqOyifnCXCpMNu+vZ6DG9TJ6CO2c2kPDSLqERTTWrO7OZj8EkYHQqSd78Yw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.2.2", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.2.2", - "jest-message-util": "^29.2.1", - "jest-util": "^29.2.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "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==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-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==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "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==", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "dependencies": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "engines": { - "node": ">= 0.4.0" - } - }, - "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==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/final-form": { - "version": "4.20.7", - "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.7.tgz", - "integrity": "sha512-ii3X9wNfyBYFnDPunYN5jh1/HAvtOZ9aJI/TVk0MB86hZuOeYkb+W5L3icgwW9WWNztZR6MDU3En6eoZTUoFPg==", - "dependencies": { - "@babel/runtime": "^7.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/final-form" - } - }, - "node_modules/final-form-set-field-touched": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/final-form-set-field-touched/-/final-form-set-field-touched-1.0.1.tgz", - "integrity": "sha512-yvE5AAs9U3OgJQ9YF8NhSF0I0mJEECvOpkaXNqovloxji5Q6gOZ0DCIAyLAKHluGSpsXKUGORyBm8Hq0beZIqQ==", - "peerDependencies": { - "final-form": ">=1.2.0" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" - }, - "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/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", - "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", - "dependencies": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "engines": { - "node": ">=10", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "eslint": ">= 6", - "typescript": ">= 2.7", - "vue-template-compiler": "*", - "webpack": ">= 4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - }, - "vue-template-compiler": { - "optional": true - } - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==" - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" - }, - "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==" - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "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", - "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/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "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==", - "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==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "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==", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "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==" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "node_modules/gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dependencies": { - "duplexer": "^0.1.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "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/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, - "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==" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "webpack": "^5.20.0" - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "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==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/husky": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", - "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", - "dev": true, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/i18next": { - "version": "22.0.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.0.4.tgz", - "integrity": "sha512-TOp7BTMKDbUkOHMzDlVsCYWpyaFkKakrrO3HNXfSz4EeJaWwnBScRmgQSTaWHScXVHBUFXTvShrCW8uryBYFcg==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "dependencies": { - "@babel/runtime": "^7.17.2" - } - }, - "node_modules/i18next-browser-languagedetector": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.0.tgz", - "integrity": "sha512-RrH7z5/DbhzhgCLDFIKXBTZlb2aXi38ZHa5e5oZaPt9zGLWmgAX49mzkQL/E7R6Y9fTE8QbZFuyMV0ronu4H/Q==", - "dependencies": { - "@babel/runtime": "^7.19.4" - } - }, - "node_modules/i18next-icu": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/i18next-icu/-/i18next-icu-2.0.3.tgz", - "integrity": "sha512-sZ0VCWDnHysUYQL8j/0rVOxv6rLR+SBoaqQQ2UVNfLyJCuf/bAjYPkoUQgyuDkWFo1xZjeCf4G6GBNr7gD61bQ==", - "peerDependencies": { - "intl-messageformat": "^9.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/idb": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.0.tgz", - "integrity": "sha512-Wsk07aAxDsntgYJY4h0knZJuTxM73eQ4reRAO+Z1liOh8eMCJ/MoDS8fCui1vGT9mnjtl1sOu3I2i/W1swPYZg==" - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "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==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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==", - "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==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/intl-messageformat": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.2.1.tgz", - "integrity": "sha512-1lrJG2qKzcC1TVzYu1VuB1yiY68LU5rwpbHa2THCzA67Vutkz7+1lv5U20K3Lz5RAiH78zxNztMEtchokMWv8A==", - "dependencies": { - "@formatjs/ecma402-abstract": "1.13.0", - "@formatjs/fast-memoize": "1.2.6", - "@formatjs/icu-messageformat-parser": "2.1.10", - "tslib": "2.4.0" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "engines": { - "node": ">=8" - } - }, - "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==", - "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/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jake/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jake/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jake/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==" - }, - "node_modules/jake/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jake/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.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": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dependencies": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-changed-files/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==" - }, - "node_modules/jest-changed-files/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/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==" - }, - "node_modules/jest-circus/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-circus/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "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/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/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==" - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-config/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==" - }, - "node_modules/jest-config/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-config/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.2.1.tgz", - "integrity": "sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.2.0", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/node_modules/pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/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/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-each/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==" - }, - "node_modules/jest-each/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-each/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-jsdom/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-jsdom/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==" - }, - "node_modules/jest-environment-jsdom/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-jsdom/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-environment-node/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-node/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-environment-node/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==" - }, - "node_modules/jest-environment-node/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-environment-node/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-get-type": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", - "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-haste-map/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==" - }, - "node_modules/jest-haste-map/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-haste-map/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-jasmine2/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==" - }, - "node_modules/jest-jasmine2/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-jasmine2/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz", - "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.2.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/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/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.2.1.tgz", - "integrity": "sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.2.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.2.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/node_modules/pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/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/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-mock/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-mock/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-mock/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-mock/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==" - }, - "node_modules/jest-mock/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dependencies": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve-dependencies/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve-dependencies/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==" - }, - "node_modules/jest-resolve-dependencies/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/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==" - }, - "node_modules/jest-resolve/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-resolve/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/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==" - }, - "node_modules/jest-runner/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runner/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/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==" - }, - "node_modules/jest-runtime/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-runtime/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dependencies": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/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==" - }, - "node_modules/jest-snapshot/node_modules/diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.2.1.tgz", - "integrity": "sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g==", - "dev": true, - "dependencies": { - "@jest/types": "^29.2.1", - "@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-util/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/jest-util/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/jest-util/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/jest-util/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/jest-util/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/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dependencies": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/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==" - }, - "node_modules/jest-validate/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-validate/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "dependencies": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "jest": "^27.0.0 || ^28.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dependencies": { - "@sinclair/typebox": "^0.24.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dependencies": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dependencies": { - "@jest/schemas": "^28.1.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": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==" - }, - "node_modules/jest-watch-typeahead/node_modules/emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dependencies": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dependencies": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-watch-typeahead/node_modules/jest-watcher/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==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watch-typeahead/node_modules/pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dependencies": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/jest-watch-typeahead/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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dependencies": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "engines": { - "node": ">=12.20" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/jest-watch-typeahead/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/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==" - }, - "node_modules/jest-watcher/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-watcher/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "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==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", - "engines": { - "node": ">=10" - } - }, - "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==" - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "node_modules/long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", - "dev": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.9.tgz", - "integrity": "sha512-3rm8kbrzpUGRyPKSGuk387NZOwQ90O4rI9tsWQkzNW7BLSnKGp23RsEsKK8N8QVCrtJoAMqy3spxHC4os4G6PQ==", - "dependencies": { - "fs-monkey": "^1.0.3" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=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/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", - "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", - "dependencies": { - "array.prototype.reduce": "^1.0.4", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "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==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "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==", - "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/parse-srcset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", - "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==", - "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==", - "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==", - "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==" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "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==", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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==", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/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==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/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==", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/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==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "browserslist": ">=4", - "postcss": ">=8" - } - }, - "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "dependencies": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - }, - "peerDependencies": { - "postcss": "^8.2.2" - } - }, - "node_modules/postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": ">=7.6.0" - }, - "peerDependencies": { - "postcss": "^8.4.6" - } - }, - "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-colormin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", - "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", - "dependencies": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-custom-properties": { - "version": "12.1.10", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", - "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.3" - } - }, - "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "peerDependencies": { - "postcss": "^8.1.4" - } - }, - "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.3.3" - } - }, - "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "engines": { - "node": "^12 || ^14 || >=16" - }, - "peerDependencies": { - "postcss": "^8.4" - } - }, - "node_modules/postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-merge-rules": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", - "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", - "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", - "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", - "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "dependencies": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "browserslist": ">= 4", - "postcss": ">= 8" - } - }, - "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", - "dependencies": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-opacity-percentage": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", - "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", - "funding": [ - { - "type": "kofi", - "url": "https://ko-fi.com/mrcgrtz" - }, - { - "type": "liberapay", - "url": "https://liberapay.com/mrcgrtz" - } - ], - "engines": { - "node": "^12 || ^14 || >=16" - } - }, - "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", - "dependencies": { - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "peerDependencies": { - "postcss": "^8" - } - }, - "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-preset-env": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.2.tgz", - "integrity": "sha512-rSMUEaOCnovKnwc5LvBDHUDzpGP+nrUeWZGWt9M72fBvckCi45JmnJigUr4QG4zZeOHmOCNCZnd2LKDvP++ZuQ==", - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.1.0", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.11", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.1", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.9", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.2.0", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-reduce-initial": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", - "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "peerDependencies": { - "postcss": "^8.0.3" - } - }, - "node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.10" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", - "dependencies": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/postcss-svgo/node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/postcss-svgo/node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "node_modules/postcss-svgo/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss-svgo/node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", - "dependencies": { - "postcss-selector-parser": "^6.0.5" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "dependencies": { - "asap": "~2.0.6" - } - }, - "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==", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "dependencies": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dependencies": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/react-dev-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/react-dev-utils/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==" - }, - "node_modules/react-dev-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/react-dev-utils/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "node_modules/react-final-form": { - "version": "6.5.9", - "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz", - "integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==", - "dependencies": { - "@babel/runtime": "^7.15.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/final-form" - }, - "peerDependencies": { - "final-form": "^4.20.4", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-final-form-listeners": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/react-final-form-listeners/-/react-final-form-listeners-1.0.3.tgz", - "integrity": "sha512-OrdCNxSS4JQS/EXD+R530kZKFqaPfa+WcXPgVro/h4BpaBDF/Ja+BtHyCzDezCIb5rWaGGdOJIj+tN2YdtvrXg==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "final-form": ">=4.0.0", - "prop-types": "^15.6.0", - "react": "^15.3.0 || ^16.0.0 || ^17.0.0", - "react-final-form": ">=3.0.0" - } - }, - "node_modules/react-i18next": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.0.0.tgz", - "integrity": "sha512-/O7N6aIEAl1FaWZBNvhdIo9itvF/MO/nRKr9pYqRc9LhuC1u21SlfwpiYQqvaeNSEW3g3qUXLREOWMt+gxrWbg==", - "dependencies": { - "@babel/runtime": "^7.14.5", - "html-parse-stringify": "^3.0.1" - }, - "peerDependencies": { - "i18next": ">= 19.0.0", - "react": ">= 16.8.0" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/react-redux": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", - "integrity": "sha512-yMfQ7mX6bWuicz2fids6cR1YT59VTuT8MKyyE310wJQlINKENCeT1UcPdEiX6znI5tF8zXyJ/VYvDgeGuaaNwQ==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz", - "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==", - "dependencies": { - "@remix-run/router": "1.0.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz", - "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==", - "dependencies": { - "@remix-run/router": "1.0.2", - "react-router": "6.4.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "dependencies": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "bin": { - "react-scripts": "bin/react-scripts.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - }, - "peerDependencies": { - "react": ">= 16", - "typescript": "^3.2.1 || ^4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/react-scripts/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/react-virtualized-auto-sizer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz", - "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==", - "engines": { - "node": ">8.0.0" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc", - "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" - } - }, - "node_modules/react-window": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz", - "integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==", - "dependencies": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - }, - "engines": { - "node": ">8.0.0" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-form": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.3.8.tgz", - "integrity": "sha512-PzXhA0d+awIc4PkuhbDa6dCEiraMrGMyyDlYEVNX6qEyW/G2SqZXrjav5zrpXb0CCeqQSc9iqwbMtYQXbJbOAQ==", - "dependencies": { - "@babel/runtime": "^7.9.2", - "es6-error": "^4.1.1", - "hoist-non-react-statics": "^3.3.2", - "invariant": "^2.2.4", - "is-promise": "^2.1.0", - "lodash": "^4.17.15", - "prop-types": "^15.6.1", - "react-is": "^16.4.2" - }, - "engines": { - "node": ">=8.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/redux-form" - }, - "peerDependencies": { - "immutable": "^3.8.2 || ^4.0.0", - "react": "^16.4.2 || ^17.0.0", - "react-redux": "^6.0.1 || ^7.0.0", - "redux": "^3.7.2 || ^4.0.0" - }, - "peerDependenciesMeta": { - "immutable": { - "optional": true - } - } - }, - "node_modules/redux-form/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/redux-thunk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==", - "peerDependencies": { - "redux": "^4" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/regexpu-core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", - "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", - "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "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==", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=8.9" - }, - "peerDependencies": { - "rework": "1.0.1", - "rework-visit": "1.0.0" - }, - "peerDependenciesMeta": { - "rework": { - "optional": true - }, - "rework-visit": { - "optional": true - } - } - }, - "node_modules/resolve-url-loader/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "node_modules/resolve-url-loader/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0" - } - }, - "node_modules/rollup-plugin-terser/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup-plugin-terser/node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/rollup-plugin-terser/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==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", - "dependencies": { - "deepmerge": "^4.2.2", - "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", - "is-plain-object": "^5.0.0", - "parse-srcset": "^1.0.2", - "postcss": "^8.3.11" - } - }, - "node_modules/sanitize-html/node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/sanitize-html/node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/sanitize-html/node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/sanitize-html/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" - }, - "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" - }, - "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", - "dependencies": { - "node-forge": "^1" - }, - "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==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", - "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", - "dependencies": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, - "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "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==", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" - }, - "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==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/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==" - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "dependencies": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", - "engines": { - "node": ">=10" - } - }, - "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==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", - "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" - } - }, - "node_modules/stylis": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", - "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/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==", - "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==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "node_modules/svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", - "dependencies": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "node_modules/svgo/node_modules/css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dependencies": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "node_modules/svgo/node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "node_modules/tailwindcss": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.1.tgz", - "integrity": "sha512-Uw+GVSxp5CM48krnjHObqoOwlCt5Qo6nw1jlCRwfGy68dSYb/LwS9ZFidYGRiM+w6rMawkZiu1mEMAsHYAfoLg==", - "dependencies": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.17", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=12.13.0" - }, - "peerDependencies": { - "postcss": "^8.0.9" - } - }, - "node_modules/tailwindcss/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==" - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "dependencies": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/terser": { - "version": "5.39.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz", - "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "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==", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "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==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" - }, - "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.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==", - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "engines": { - "node": ">=10.4" - } - }, - "node_modules/webpack": { - "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.19.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/webpack-dev-middleware/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", - "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack-dev-server/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/webpack-dev-server/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "dependencies": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "webpack": "^4.44.2 || ^5.47.0" - } - }, - "node_modules/webpack-manifest-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "dependencies": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-background-sync": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", - "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-broadcast-update": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", - "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-build": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", - "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", - "dependencies": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.5.4", - "workbox-broadcast-update": "6.5.4", - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-google-analytics": "6.5.4", - "workbox-navigation-preload": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-range-requests": "6.5.4", - "workbox-recipes": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4", - "workbox-streams": "6.5.4", - "workbox-sw": "6.5.4", - "workbox-window": "6.5.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/workbox-build/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/workbox-build/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/workbox-build/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/workbox-build/node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/workbox-build/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/workbox-build/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" - }, - "node_modules/workbox-build/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/workbox-cacheable-response": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", - "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-core": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", - "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" - }, - "node_modules/workbox-expiration": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", - "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", - "dependencies": { - "idb": "^7.0.1", - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-google-analytics": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", - "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", - "dependencies": { - "workbox-background-sync": "6.5.4", - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "node_modules/workbox-navigation-preload": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", - "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-precaching": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", - "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", - "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "node_modules/workbox-range-requests": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", - "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-recipes": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", - "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", - "dependencies": { - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "node_modules/workbox-routing": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", - "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-strategies": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", - "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", - "dependencies": { - "workbox-core": "6.5.4" - } - }, - "node_modules/workbox-streams": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", - "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", - "dependencies": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4" - } - }, - "node_modules/workbox-sw": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", - "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" - }, - "node_modules/workbox-webpack-plugin": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", - "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", - "dependencies": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.5.4" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": "^4.4.0 || ^5.9.0" - } - }, - "node_modules/workbox-webpack-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/workbox-webpack-plugin/node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "node_modules/workbox-window": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", - "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", - "dependencies": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.5.4" - } - }, - "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==", - "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/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/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==" - }, - "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==" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "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==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@adobe/css-tools": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", - "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@apideck/better-ajv-errors": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", - "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", - "requires": { - "json-schema": "^0.4.0", - "jsonpointer": "^5.0.0", - "leven": "^3.1.0" - } - }, - "@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==", - "requires": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - } - }, - "@babel/compat-data": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.0.tgz", - "integrity": "sha512-Gt9jszFJYq7qzXVK4slhc6NzJXnOVmRECWcVjF/T23rNXD9NtWQ0W3qxdg+p9wWIB+VQw3GYV/U2Ha9bRTfs4w==" - }, - "@babel/core": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.6.tgz", - "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==", - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.6", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helpers": "^7.19.4", - "@babel/parser": "^7.19.6", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - } - }, - "@babel/eslint-parser": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz", - "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", - "requires": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" - } - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", - "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", - "requires": { - "@babel/helper-explode-assignable-expression": "^7.18.6", - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", - "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", - "requires": { - "@babel/compat-data": "^7.20.0", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "semver": "^6.3.0" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", - "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", - "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", - "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", - "requires": { - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz", - "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==", - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.19.4", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.6", - "@babel/types": "^7.19.4" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-replace-supers": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz", - "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.19.1", - "@babel/types": "^7.19.0" - } - }, - "@babel/helper-simple-access": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz", - "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==", - "requires": { - "@babel/types": "^7.19.4" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "requires": { - "@babel/types": "^7.20.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@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==" - }, - "@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==" - }, - "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" - }, - "@babel/helper-wrap-function": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", - "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", - "requires": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.0", - "@babel/types": "^7.19.0" - } - }, - "@babel/helpers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", - "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", - "requires": { - "@babel/template": "^7.27.1", - "@babel/types": "^7.27.1" - } - }, - "@babel/parser": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", - "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", - "requires": { - "@babel/types": "^7.27.1" - } - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.19.1.tgz", - "integrity": "sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==", - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.0.tgz", - "integrity": "sha512-vnuRRS20ygSxclEYikHzVrP9nZDFXaSzvJxGLQNAiBX041TmhS4hOUHWNIpq/q4muENuEP9XPJFXTNFejhemkg==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.19.1", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.19.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz", - "integrity": "sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==", - "requires": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", - "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", - "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@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==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.0.tgz", - "integrity": "sha512-sXOohbpHZSk7GjxK9b3dKB7CfqUD5DwOH+DggKzOQ7TXYP+RCSbRykfjQmn/zq+rBjycVRtLf9pYhAaEJA786w==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", - "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.19.0", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-replace-supers": "^7.18.9", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.0.tgz", - "integrity": "sha512-1dIhvZfkDVx/zn2S1aFwlruspTt4189j7fEkH0Y0VyuDM6bQt7bD6kLcz3l4IlLG+e5OReaBz9ROAbttRtUHqA==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz", - "integrity": "sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-flow": "^7.18.6" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", - "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", - "requires": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", - "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", - "requires": { - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", - "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", - "requires": { - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-simple-access": "^7.19.4" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", - "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", - "requires": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.19.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-identifier": "^7.19.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", - "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.1.tgz", - "integrity": "sha512-oWk9l9WItWBQYS4FgXD4Uyy5kq898lvkXpXQxoJEY1RnvPk4R/Dvu2ebXU9q8lP+rlMwUQTFf2Ok6d78ODa0kw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.12.tgz", - "integrity": "sha512-Q99U9/ttiu+LMnRU8psd23HhvwXmKWDQIpocm0JKaICcZHnw+mdQbHm6xnSy7dOl8I5PELakYtNBubNQlBXbZw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", - "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.19.0" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.18.6" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", - "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", - "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", - "requires": { - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz", - "integrity": "sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.19.0", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/plugin-syntax-typescript": "^7.20.0" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", - "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/preset-env": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.19.4.tgz", - "integrity": "sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==", - "requires": { - "@babel/compat-data": "^7.19.4", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-plugin-utils": "^7.19.0", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.19.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.19.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", - "@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", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.19.4", - "@babel/plugin-transform-classes": "^7.19.0", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.19.4", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.0", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.19.4", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" - } - }, - "@babel/preset-typescript": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", - "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-typescript": "^7.18.6" - } - }, - "@babel/runtime": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", - "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==" - }, - "@babel/runtime-corejs3": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.1.tgz", - "integrity": "sha512-909rVuj3phpjW6y0MCXAZ5iNeORePa6ldJvp2baWGcTjwqbBDDz6xoS5JHJ7lS88NlwLYj07ImL/8IUMtDZzTA==", - "requires": { - "core-js-pure": "^3.30.2" - } - }, - "@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "requires": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - } - }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", - "requires": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" - }, - "@csstools/normalize.css": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", - "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" - }, - "@csstools/postcss-cascade-layers": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", - "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", - "requires": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - } - }, - "@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", - "requires": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - } - }, - "@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" - }, - "@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==" - }, - "@emotion/babel-plugin": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", - "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==", - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.1", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.1.3" - } - }, - "@emotion/cache": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", - "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", - "requires": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.1", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.1.3" - } - }, - "@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, - "@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", - "requires": { - "@emotion/memoize": "^0.8.0" - } - }, - "@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" - }, - "@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", - "requires": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", - "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" - }, - "@emotion/styled": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.5.tgz", - "integrity": "sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/is-prop-valid": "^1.2.0", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0" - } - }, - "@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==" - }, - "@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" - }, - "@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" - }, - "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - } - } - }, - "@formatjs/ecma402-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.13.0.tgz", - "integrity": "sha512-CQ8Ykd51jYD1n05dtoX6ns6B9n/+6ZAxnWUAonvHC4kkuAemROYBhHkEB4tm1uVrRlE7gLDqXkAnY51Y0pRCWQ==", - "requires": { - "@formatjs/intl-localematcher": "0.2.31", - "tslib": "2.4.0" - } - }, - "@formatjs/fast-memoize": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.6.tgz", - "integrity": "sha512-9CWZ3+wCkClKHX+i5j+NyoBVqGf0pIskTo6Xl6ihGokYM2yqSSS68JIgeo+99UIHc+7vi9L3/SDSz/dWI9SNlA==", - "requires": { - "tslib": "2.4.0" - } - }, - "@formatjs/icu-messageformat-parser": { - "version": "2.1.10", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.10.tgz", - "integrity": "sha512-KkRMxhifWkRC45dhM9tqm0GXbb6NPYTGVYY3xx891IKc6p++DQrZTnmkVSNNO47OEERLfuP2KkPFPJBuu8z/wg==", - "requires": { - "@formatjs/ecma402-abstract": "1.13.0", - "@formatjs/icu-skeleton-parser": "1.3.14", - "tslib": "2.4.0" - } - }, - "@formatjs/icu-skeleton-parser": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.14.tgz", - "integrity": "sha512-7bv60HQQcBb3+TSj+45tOb/CHV5z1hOpwdtS50jsSBXfB+YpGhnoRsZxSRksXeCxMy6xn6tA6VY2601BrrK+OA==", - "requires": { - "@formatjs/ecma402-abstract": "1.13.0", - "tslib": "2.4.0" - } - }, - "@formatjs/intl-localematcher": { - "version": "0.2.31", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.31.tgz", - "integrity": "sha512-9QTjdSBpQ7wHShZgsNzNig5qT3rCPvmZogS/wXZzKotns5skbXgs0I7J8cuN0PPqXyynvNVuN+iOKhNS2eb+ZA==", - "requires": { - "tslib": "2.4.0" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==" - }, - "@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==", - "requires": { - "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" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "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==", - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - } - } - }, - "@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==" - }, - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "requires": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/expect-utils": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.2.2.tgz", - "integrity": "sha512-vwnVmrVhTmGgQzyvcpze08br91OL61t9O0lJMDyb6Y/D8EKQ9V7rGUb/p7PDt0GPzK0zFYqXWFo4EO2legXmkg==", - "dev": true, - "requires": { - "jest-get-type": "^29.2.0" - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/schemas": { - "version": "29.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", - "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "requires": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/types": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.2.1.tgz", - "integrity": "sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" - }, - "@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" - }, - "@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "requires": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - } - } - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" - }, - "@mui/base": { - "version": "5.0.0-alpha.103", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.103.tgz", - "integrity": "sha512-fJIyB2df3CHn7D26WHnutnY7vew6aytTlhmRJz6GX7ag19zU2GcOUhJAzY5qwWcrXKnlYgzimhEjaEnuiUWU4g==", - "requires": { - "@babel/runtime": "^7.19.0", - "@emotion/is-prop-valid": "^1.2.0", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "@popperjs/core": "^2.11.6", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - } - }, - "@mui/core-downloads-tracker": { - "version": "5.10.11", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.10.11.tgz", - "integrity": "sha512-u5ff+UCFDHcR8MoQ8tuJR4c35vt7T/ki3aMEE2O3XQoGs8KJSrBiisFpFKyldg9/W2NSyoZxN+kxEGIfRxh+9Q==" - }, - "@mui/icons-material": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.10.9.tgz", - "integrity": "sha512-sqClXdEM39WKQJOQ0ZCPTptaZgqwibhj2EFV9N0v7BU1PO8y4OcX/a2wIQHn4fNuDjIZktJIBrmU23h7aqlGgg==", - "requires": { - "@babel/runtime": "^7.19.0" - } - }, - "@mui/material": { - "version": "5.10.11", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.10.11.tgz", - "integrity": "sha512-KJ0wPCTbv6sFzwA3dgg0gowdfF+SRl7D510J9l6Nl/KFX0EawcewQudqKY4slYGFXniKa5PykqokpaWXsCCPqg==", - "requires": { - "@babel/runtime": "^7.19.0", - "@mui/base": "5.0.0-alpha.103", - "@mui/core-downloads-tracker": "^5.10.11", - "@mui/system": "^5.10.10", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "@types/react-transition-group": "^4.4.5", - "clsx": "^1.2.1", - "csstype": "^3.1.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - } - }, - "@mui/private-theming": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.10.9.tgz", - "integrity": "sha512-BN7/CnsVPVyBaQpDTij4uV2xGYHHHhOgpdxeYLlIu+TqnsVM7wUeF+37kXvHovxM6xmL5qoaVUD98gDC0IZnHg==", - "requires": { - "@babel/runtime": "^7.19.0", - "@mui/utils": "^5.10.9", - "prop-types": "^15.8.1" - } - }, - "@mui/styled-engine": { - "version": "5.10.8", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.10.8.tgz", - "integrity": "sha512-w+y8WI18EJV6zM/q41ug19cE70JTeO6sWFsQ7tgePQFpy6ToCVPh0YLrtqxUZXSoMStW5FMw0t9fHTFAqPbngw==", - "requires": { - "@babel/runtime": "^7.19.0", - "@emotion/cache": "^11.10.3", - "csstype": "^3.1.1", - "prop-types": "^15.8.1" - } - }, - "@mui/system": { - "version": "5.10.10", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.10.10.tgz", - "integrity": "sha512-TXwtKN0adKpBrZmO+eilQWoPf2veh050HLYrN78Kps9OhlvO70v/2Kya0+mORFhu9yhpAwjHXO8JII/R4a5ZLA==", - "requires": { - "@babel/runtime": "^7.19.0", - "@mui/private-theming": "^5.10.9", - "@mui/styled-engine": "^5.10.8", - "@mui/types": "^7.2.0", - "@mui/utils": "^5.10.9", - "clsx": "^1.2.1", - "csstype": "^3.1.1", - "prop-types": "^15.8.1" - } - }, - "@mui/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.0.tgz", - "integrity": "sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA==" - }, - "@mui/utils": { - "version": "5.10.9", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.10.9.tgz", - "integrity": "sha512-2tdHWrq3+WCy+G6TIIaFx3cg7PorXZ71P375ExuX61od1NOAJP1mK90VxQ8N4aqnj2vmO3AQDkV4oV2Ktvt4bA==", - "requires": { - "@babel/runtime": "^7.19.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^16.7.1 || ^17.0.0", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - } - }, - "@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "requires": { - "eslint-scope": "5.1.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.8.tgz", - "integrity": "sha512-wxXRwf+IQ6zvHSJZ+5T2RQNEsq+kx4jKRXfFvdt3nBIUzJUAvXEFsUeoaohDe/Kr84MTjGwcuIUPNcstNJORsA==", - "requires": { - "ansi-html-community": "^0.0.8", - "common-path-prefix": "^3.0.0", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "find-up": "^5.0.0", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" - } - } - }, - "@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" - }, - "@remix-run/router": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.2.tgz", - "integrity": "sha512-GRSOFhJzjGN+d4sKHTMSvNeUPoZiDHWmRnXfzaxrqe7dE/Nzlc8BiMSJdLDESZlndM7jIUrZ/F4yWqVYlI0rwQ==" - }, - "@rollup/plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", - "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - } - }, - "@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - } - }, - "@rollup/plugin-replace": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", - "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", - "requires": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" - } - } - }, - "@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" - }, - "@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@surma/rollup-plugin-off-main-thread": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", - "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", - "requires": { - "ejs": "^3.1.6", - "json5": "^2.2.0", - "magic-string": "^0.25.0", - "string.prototype.matchall": "^4.0.6" - } - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", - "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", - "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", - "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", - "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", - "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", - "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", - "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" - }, - "@svgr/babel-preset": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", - "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", - "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", - "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", - "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", - "@svgr/babel-plugin-transform-svg-component": "^5.5.0" - } - }, - "@svgr/core": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", - "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", - "requires": { - "@svgr/plugin-jsx": "^5.5.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.0" - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", - "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", - "requires": { - "@babel/types": "^7.12.6" - } - }, - "@svgr/plugin-jsx": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", - "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", - "requires": { - "@babel/core": "^7.12.3", - "@svgr/babel-preset": "^5.5.0", - "@svgr/hast-util-to-babel-ast": "^5.5.0", - "svg-parser": "^2.0.2" - } - }, - "@svgr/plugin-svgo": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", - "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", - "requires": { - "cosmiconfig": "^7.0.0", - "deepmerge": "^4.2.2", - "svgo": "^1.2.2" - } - }, - "@svgr/webpack": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", - "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", - "requires": { - "@babel/core": "^7.12.3", - "@babel/plugin-transform-react-constant-elements": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@babel/preset-react": "^7.12.5", - "@svgr/core": "^5.5.0", - "@svgr/plugin-jsx": "^5.5.0", - "@svgr/plugin-svgo": "^5.5.0", - "loader-utils": "^2.0.0" - } - }, - "@testing-library/dom": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz", - "integrity": "sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^4.2.0", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", - "dev": true, - "requires": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" - }, - "@types/aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", - "dev": true - }, - "@types/babel__core": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", - "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.2.tgz", - "integrity": "sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg==", - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/bonjour": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", - "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", - "requires": { - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/connect-history-api-fallback": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", - "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", - "requires": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "@types/eslint": { - "version": "8.4.9", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.9.tgz", - "integrity": "sha512-jFCSo4wJzlHQLCpceUhUnXdrPuCNOjGFMQ8Eg6JXxlz3QaCKOb7eGi2cephQdM4XTYsNej69P9JDJ1zqNIbncQ==", - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==" - }, - "@types/express": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", - "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.31", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", - "requires": { - "@types/node": "*" - } - }, - "@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", - "dev": true - }, - "@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "requires": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" - }, - "@types/http-proxy": { - "version": "1.17.9", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", - "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.2.0.tgz", - "integrity": "sha512-KO7bPV21d65PKwv3LLsD8Jn3E05pjNjRZvkm+YTacWhVmykAb07wW6IkZUmQAltwQafNcDUEUrMO2h3jeBSisg==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - }, - "dependencies": { - "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 - }, - "pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - } - } - }, - "@types/jquery": { - "version": "3.5.14", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", - "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", - "dev": true, - "requires": { - "@types/sizzle": "*" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "@types/lodash": { - "version": "4.14.186", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", - "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", - "dev": true - }, - "@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" - }, - "@types/node": { - "version": "18.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.7.tgz", - "integrity": "sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "@types/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==" - }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "@types/q": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", - "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/react": { - "version": "18.0.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", - "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "18.0.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.8.tgz", - "integrity": "sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-is": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", - "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", - "requires": { - "@types/react": "*" - } - }, - "@types/react-redux": { - "version": "7.1.24", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", - "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", - "dev": true, - "requires": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, - "@types/react-router": { - "version": "5.1.19", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.19.tgz", - "integrity": "sha512-Fv/5kb2STAEMT3wHzdKQK2z8xKq38EDIGVrutYLmQVVLe+4orDFquU52hQrULnEHinMKv9FSA6lf9+uNT1ITtA==", - "dev": true, - "requires": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "dev": true, - "requires": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", - "requires": { - "@types/react": "*" - } - }, - "@types/react-virtualized-auto-sizer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz", - "integrity": "sha512-GH8sAnBEM5GV9LTeiz56r4ZhMOUSrP43tAQNSRVxNexDjcNKLCEtnxusAItg1owFUFE6k0NslV26gqVClVvong==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-window": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.5.tgz", - "integrity": "sha512-V9q3CvhC9Jk9bWBOysPGaWy/Z0lxYcTXLtLipkt2cnRj1JOSFNF7wqGpkScSXMgBwC+fnVRg/7shwgddBG5ICw==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/redux-form": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/@types/redux-form/-/redux-form-8.3.5.tgz", - "integrity": "sha512-SchB4i7nxgWNbJS4cXEZducztkvHzVrb5xlAXwfLpbrLPo6tMY06+kx1GqMv42+YnGy9TpCAkF51a21HatqWBA==", - "dev": true, - "requires": { - "@types/react": "*", - "redux": "^3.6.0 || ^4.0.0" - } - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "requires": { - "@types/node": "*" - } - }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, - "@types/scheduler": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" - }, - "@types/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", - "requires": { - "@types/express": "*" - } - }, - "@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", - "requires": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "@types/sockjs": { - "version": "0.3.33", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", - "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" - }, - "@types/testing-library__jest-dom": { - "version": "5.14.5", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz", - "integrity": "sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ==", - "dev": true, - "requires": { - "@types/jest": "*" - } - }, - "@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" - }, - "@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" - }, - "@types/ws": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", - "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "17.0.13", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", - "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", - "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", - "requires": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/type-utils": "5.41.0", - "@typescript-eslint/utils": "5.41.0", - "debug": "^4.3.4", - "ignore": "^5.2.0", - "regexpp": "^3.2.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.41.0.tgz", - "integrity": "sha512-/qxT2Kd2q/A22JVIllvws4rvc00/3AT4rAo/0YgEN28y+HPhbJbk6X4+MAHEoZzpNyAOugIT7D/OLnKBW8FfhA==", - "requires": { - "@typescript-eslint/utils": "5.41.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", - "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", - "requires": { - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", - "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", - "requires": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", - "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", - "requires": { - "@typescript-eslint/typescript-estree": "5.41.0", - "@typescript-eslint/utils": "5.41.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", - "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==" - }, - "@typescript-eslint/typescript-estree": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", - "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", - "requires": { - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/visitor-keys": "5.41.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", - "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", - "requires": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.41.0", - "@typescript-eslint/types": "5.41.0", - "@typescript-eslint/typescript-estree": "5.41.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", - "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", - "requires": { - "@typescript-eslint/types": "5.41.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "requires": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==" - }, - "@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==" - }, - "@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==" - }, - "@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==" - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==" - }, - "@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "requires": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==" - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - } - } - }, - "acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" - }, - "acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "requires": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" - }, - "address": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz", - "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==" - }, - "adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - } - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" - }, - "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==", - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.2.tgz", - "integrity": "sha512-JWydkr9MirMg2jGJstDqDgzoHqaFbv7n1ghfXYdtEgXWgdq3jz7IU3SQvtj9k3mAszQBiTpQhFdlH+JIRuGTzg==", - "dev": true, - "requires": { - "deep-equal": "^2.0.5" - } - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" - }, - "array-includes": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", - "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" - }, - "array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.reduce": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", - "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - } - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" - }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", - "requires": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "axe-core": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.0.tgz", - "integrity": "sha512-4+rr8eQ7+XXS5nZrKcMO/AikHL0hVqy+lHWAnE3xdHl+aguag8SOQ6eEqLexwLNWgXIMfunGuD3ON1/6Kyet0A==" - }, - "axobject-query": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", - "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==" - }, - "babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "requires": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "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==", - "requires": { - "@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" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, - "babel-plugin-named-asset-import": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "requires": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "requires": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==" - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" - }, - "bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", - "requires": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "bonjour-service": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", - "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", - "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "requires": { - "fill-range": "^7.1.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" - }, - "browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "requires": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "requires": { - "node-int64": "^0.4.0" - } - }, - "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==" - }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" - }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==" - }, - "case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - } - } - }, - "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==" - }, - "check-types": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", - "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" - }, - "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==" - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==" - }, - "clean-css": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", - "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", - "requires": { - "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" - }, - "colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" - }, - "common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" - }, - "common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" - }, - "connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "core-js": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz", - "integrity": "sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw==" - }, - "core-js-compat": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz", - "integrity": "sha512-piOX9Go+Z4f9ZiBFLnZ5VrOpBl0h7IGCkiFUN11QTe6LjAvOT3ifL/5TdoizMh99hcGy5SoLyWbapIY/PIb/3A==", - "requires": { - "browserslist": "^4.21.4" - } - }, - "core-js-pure": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", - "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==" - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" - }, - "css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-declaration-sorter": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" - }, - "css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", - "requires": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.7", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "css-minimizer-webpack-plugin": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", - "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", - "requires": { - "cssnano": "^5.0.6", - "jest-worker": "^27.0.2", - "postcss": "^8.3.5", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" - }, - "css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" - }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true - }, - "cssdb": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.2.tgz", - "integrity": "sha512-Vm4b6P/PifADu0a76H0DKRNVWq3Rq9xa/Nx6oEMUBJlwTUuZoZ3dkZxo8Gob3UEL53Cq+Ma1GBgISed6XEBs3w==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "cssnano": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.14.tgz", - "integrity": "sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==", - "requires": { - "cssnano-preset-default": "^5.2.13", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" - } - }, - "cssnano-preset-default": { - "version": "5.2.13", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.13.tgz", - "integrity": "sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==", - "requires": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.0", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.3", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.1", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - } - }, - "cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "requires": { - "css-tree": "^1.1.2" - }, - "dependencies": { - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" - } - } - }, - "csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.2.tgz", - "integrity": "sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==" - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" - }, - "deep-equal": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", - "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "es-get-iterator": "^1.1.1", - "get-intrinsic": "^1.0.1", - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.2", - "is-regex": "^1.1.1", - "isarray": "^2.0.5", - "object-is": "^1.1.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.3", - "which-boxed-primitive": "^1.0.1", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.2" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "requires": { - "execa": "^5.0.0" - } - }, - "define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "defined": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz", - "integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "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==" - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" - }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "detective": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", - "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", - "requires": { - "acorn-node": "^1.8.2", - "defined": "^1.0.0", - "minimist": "^1.2.6" - } - }, - "dexie": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.2.tgz", - "integrity": "sha512-q5dC3HPmir2DERlX+toCBbHQXW5MsyrFqPFcovkH9N2S/UW/H3H5AWAB6iEOExeraAu+j+zRDG+zg/D7YhH0qg==" - }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "diff-sequences": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.2.0.tgz", - "integrity": "sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "requires": { - "path-type": "^4.0.0" - } - }, - "dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==" - }, - "dns-packet": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", - "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", - "requires": { - "@leichtgewicht/ip-codec": "^2.0.1" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-accessibility-api": { - "version": "0.5.14", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", - "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", - "dev": true - }, - "dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "requires": { - "utila": "~0.4" - } - }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" - } - } - }, - "domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "requires": { - "domelementtype": "^2.2.0" - } - }, - "domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "requires": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "requires": { - "jake": "^10.8.5" - } - }, - "electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==" - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" - }, - "encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" - }, - "enhanced-resolve": { - "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - } - }, - "entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" - }, - "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==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "requires": { - "stackframe": "^1.3.4" - } - }, - "es-abstract": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", - "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.5", - "string.prototype.trimstart": "^1.0.5", - "unbox-primitive": "^1.0.2" - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-get-iterator": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz", - "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.0", - "has-symbols": "^1.0.1", - "is-arguments": "^1.1.0", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.5", - "isarray": "^2.0.5" - } - }, - "es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==" - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, - "escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "eslint": { - "version": "8.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", - "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.15.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - } - } - }, - "eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "requires": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", - "requires": { - "debug": "^3.2.7", - "resolve": "^1.20.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "requires": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "requires": { - "@typescript-eslint/experimental-utils": "^5.0.0" - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz", - "integrity": "sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==", - "requires": { - "@babel/runtime": "^7.18.9", - "aria-query": "^4.2.2", - "array-includes": "^3.1.5", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.4.3", - "axobject-query": "^2.2.0", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.2", - "language-tags": "^1.0.5", - "minimatch": "^3.1.2", - "semver": "^6.3.0" - }, - "dependencies": { - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - } - } - }, - "eslint-plugin-react": { - "version": "7.31.10", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz", - "integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==", - "requires": { - "array-includes": "^3.1.5", - "array.prototype.flatmap": "^1.3.0", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.5", - "object.fromentries": "^2.0.5", - "object.hasown": "^1.1.1", - "object.values": "^1.1.5", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.3", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.7" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "requires": { - "esutils": "^2.0.2" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" - }, - "eslint-plugin-testing-library": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.9.1.tgz", - "integrity": "sha512-6BQp3tmb79jLLasPHJmy8DnxREe+2Pgf7L+7o09TSWPfdqqtQfRZmZNetr5mOs3yqZk/MRNxpN3RUpJe0wB4LQ==", - "requires": { - "@typescript-eslint/utils": "^5.13.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==" - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" - }, - "eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", - "requires": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "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" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" - }, - "expect": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.2.2.tgz", - "integrity": "sha512-hE09QerxZ5wXiOhqkXy5d2G9ar+EqOyifnCXCpMNu+vZ6DG9TJ6CO2c2kPDSLqERTTWrO7OZj8EkYHQqSd78Yw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.2.2", - "jest-get-type": "^29.2.0", - "jest-matcher-utils": "^29.2.2", - "jest-message-util": "^29.2.1", - "jest-util": "^29.2.1" - } - }, - "express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "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==" - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "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==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==" - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", - "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - } - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" - }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "final-form": { - "version": "4.20.7", - "resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.7.tgz", - "integrity": "sha512-ii3X9wNfyBYFnDPunYN5jh1/HAvtOZ9aJI/TVk0MB86hZuOeYkb+W5L3icgwW9WWNztZR6MDU3En6eoZTUoFPg==", - "requires": { - "@babel/runtime": "^7.10.0" - } - }, - "final-form-set-field-touched": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/final-form-set-field-touched/-/final-form-set-field-touched-1.0.1.tgz", - "integrity": "sha512-yvE5AAs9U3OgJQ9YF8NhSF0I0mJEECvOpkaXNqovloxji5Q6gOZ0DCIAyLAKHluGSpsXKUGORyBm8Hq0beZIqQ==" - }, - "finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" - }, - "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==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fork-ts-checker-webpack-plugin": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", - "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" - } - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" - }, - "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==" - }, - "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==" - }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "get-own-enumerable-property-symbols": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", - "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" - }, - "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==" - }, - "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==" - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "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" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "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==" - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "requires": { - "duplexer": "^0.1.2" - } - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" - }, - "harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "requires": { - "es-define-property": "^1.0.0" - } - }, - "has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-entities": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", - "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" - }, - "html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "requires": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - } - }, - "html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "requires": { - "void-elements": "3.1.0" - } - }, - "html-webpack-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", - "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", - "requires": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - } - }, - "htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "requires": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "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==" - }, - "husky": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", - "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", - "dev": true - }, - "i18next": { - "version": "22.0.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.0.4.tgz", - "integrity": "sha512-TOp7BTMKDbUkOHMzDlVsCYWpyaFkKakrrO3HNXfSz4EeJaWwnBScRmgQSTaWHScXVHBUFXTvShrCW8uryBYFcg==", - "requires": { - "@babel/runtime": "^7.17.2" - } - }, - "i18next-browser-languagedetector": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.0.tgz", - "integrity": "sha512-RrH7z5/DbhzhgCLDFIKXBTZlb2aXi38ZHa5e5oZaPt9zGLWmgAX49mzkQL/E7R6Y9fTE8QbZFuyMV0ronu4H/Q==", - "requires": { - "@babel/runtime": "^7.19.4" - } - }, - "i18next-icu": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/i18next-icu/-/i18next-icu-2.0.3.tgz", - "integrity": "sha512-sZ0VCWDnHysUYQL8j/0rVOxv6rLR+SBoaqQQ2UVNfLyJCuf/bAjYPkoUQgyuDkWFo1xZjeCf4G6GBNr7gD61bQ==" - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" - }, - "idb": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.0.tgz", - "integrity": "sha512-Wsk07aAxDsntgYJY4h0knZJuTxM73eQ4reRAO+Z1liOh8eMCJ/MoDS8fCui1vGT9mnjtl1sOu3I2i/W1swPYZg==" - }, - "identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "requires": { - "harmony-reflect": "^1.4.6" - } - }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" - }, - "immer": { - "version": "9.0.16", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz", - "integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "intl-messageformat": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.2.1.tgz", - "integrity": "sha512-1lrJG2qKzcC1TVzYu1VuB1yiY68LU5rwpbHa2THCzA67Vutkz7+1lv5U20K3Lz5RAiH78zxNztMEtchokMWv8A==", - "requires": { - "@formatjs/ecma402-abstract": "1.13.0", - "@formatjs/fast-memoize": "1.2.6", - "@formatjs/icu-messageformat-parser": "2.1.10", - "tslib": "2.4.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "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==" - }, - "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==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==" - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" - }, - "is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", - "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" - }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" - }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", - "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" - }, - "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==", - "requires": { - "@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" - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "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==", - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.8.5", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", - "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.1", - "minimatch": "^3.0.4" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "requires": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - } - }, - "jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "requires": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "requires": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-diff": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.2.1.tgz", - "integrity": "sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.2.0", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.1" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "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 - } - } - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-get-type": { - "version": "29.2.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", - "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", - "dev": true - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "requires": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - } - } - }, - "jest-matcher-utils": { - "version": "29.2.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz", - "integrity": "sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.2.1", - "jest-get-type": "^29.2.0", - "pretty-format": "^29.2.1" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "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 - } - } - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-message-util": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.2.1.tgz", - "integrity": "sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.2.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.2.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "pretty-format": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.2.1.tgz", - "integrity": "sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA==", - "dev": true, - "requires": { - "@jest/schemas": "^29.0.0", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "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 - } - } - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==" - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "requires": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "requires": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-util": { - "version": "29.2.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.2.1.tgz", - "integrity": "sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g==", - "dev": true, - "requires": { - "@jest/types": "^29.2.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "requires": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - } - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "requires": { - "@jest/schemas": "^28.1.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" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - } - } - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==" - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "dependencies": { - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "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==" - } - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" - }, - "string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "requires": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==" - } - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "requires": { - "ansi-regex": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - } - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "requires": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-sdsl": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", - "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "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==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" - }, - "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==" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "requires": { - "language-subtag-registry": "~0.3.2" - } - }, - "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==" - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lilconfig": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", - "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" - }, - "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==" - }, - "loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==" - }, - "loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" - }, - "long": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", - "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { - "tslib": "^2.0.3" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "lz-string": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", - "dev": true - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - } - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "requires": { - "tmpl": "1.0.5" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "memfs": { - "version": "3.4.9", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.9.tgz", - "integrity": "sha512-3rm8kbrzpUGRyPKSGuk387NZOwQ90O4rI9tsWQkzNW7BLSnKGp23RsEsKK8N8QVCrtJoAMqy3spxHC4os4G6PQ==", - "requires": { - "fs-monkey": "^1.0.3" - } - }, - "memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" - }, - "merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" - }, - "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==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "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==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", - "requires": { - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "requires": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - } - }, - "nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" - }, - "node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==" - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" - }, - "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==", - "requires": { - "path-key": "^3.0.0" - } - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" - }, - "object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==" - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", - "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", - "requires": { - "array.prototype.reduce": "^1.0.4", - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.1" - } - }, - "object.hasown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz", - "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==", - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "requires": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - } - }, - "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==" - }, - "param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "requires": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "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==", - "requires": { - "@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" - } - }, - "parse-srcset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", - "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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==" - }, - "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==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "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==" - }, - "path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" - }, - "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==", - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "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==", - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - } - } - }, - "postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "requires": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - } - }, - "postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-browser-comments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" - }, - "postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", - "requires": { - "postcss-selector-parser": "^6.0.9", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-clamp": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", - "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-colormin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", - "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", - "requires": { - "browserslist": "^4.16.6", - "caniuse-api": "^3.0.0", - "colord": "^2.9.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", - "requires": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-properties": { - "version": "12.1.10", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz", - "integrity": "sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" - }, - "postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" - }, - "postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" - }, - "postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" - }, - "postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-flexbugs-fixes": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" - }, - "postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", - "requires": { - "postcss-selector-parser": "^6.0.9" - } - }, - "postcss-font-variant": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" - }, - "postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" - }, - "postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", - "requires": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-initial": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" - }, - "postcss-js": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", - "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", - "requires": { - "camelcase-css": "^2.0.1" - } - }, - "postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - } - }, - "postcss-loader": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", - "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.5" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" - }, - "postcss-media-minmax": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" - }, - "postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", - "requires": { - "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" - } - }, - "postcss-merge-rules": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.3.tgz", - "integrity": "sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==", - "requires": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", - "requires": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", - "requires": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" - }, - "postcss-modules-local-by-default": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", - "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", - "requires": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", - "requires": { - "postcss-selector-parser": "^6.0.4" - } - }, - "postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "requires": { - "icss-utils": "^5.0.0" - } - }, - "postcss-nested": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", - "integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-nesting": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", - "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", - "requires": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-normalize": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", - "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", - "requires": { - "@csstools/normalize.css": "*", - "postcss-browser-comments": "^4", - "sanitize.css": "*" - } - }, - "postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" - }, - "postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", - "requires": { - "browserslist": "^4.21.4", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", - "requires": { - "normalize-url": "^6.0.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-opacity-percentage": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", - "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==" - }, - "postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", - "requires": { - "cssnano-utils": "^3.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-page-break": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" - }, - "postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-preset-env": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.2.tgz", - "integrity": "sha512-rSMUEaOCnovKnwc5LvBDHUDzpGP+nrUeWZGWt9M72fBvckCi45JmnJigUr4QG4zZeOHmOCNCZnd2LKDvP++ZuQ==", - "requires": { - "@csstools/postcss-cascade-layers": "^1.1.0", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.11", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.1", - "postcss-attribute-case-insensitive": "^5.0.2", - "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.9", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", - "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", - "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", - "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.2.0", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", - "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", - "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-reduce-initial": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.1.tgz", - "integrity": "sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==", - "requires": { - "browserslist": "^4.21.4", - "caniuse-api": "^3.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", - "requires": { - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-replace-overflow-wrap": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" - }, - "postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", - "requires": { - "postcss-selector-parser": "^6.0.10" - } - }, - "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", - "requires": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - } - }, - "postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", - "requires": { - "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" - }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", - "requires": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" - } - } - } - }, - "postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", - "requires": { - "postcss-selector-parser": "^6.0.5" - } - }, - "postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" - }, - "pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" - }, - "pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "requires": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "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==" - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - } - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", - "requires": { - "asap": "~2.0.6" - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "dependencies": { - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - } - } - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" - }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" - }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "requires": { - "performance-now": "^2.1.0" - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-app-polyfill": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", - "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", - "requires": { - "core-js": "^3.19.2", - "object-assign": "^4.1.1", - "promise": "^8.1.0", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.9", - "whatwg-fetch": "^3.6.2" - } - }, - "react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "requires": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" - }, - "react-final-form": { - "version": "6.5.9", - "resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz", - "integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==", - "requires": { - "@babel/runtime": "^7.15.4" - } - }, - "react-final-form-listeners": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/react-final-form-listeners/-/react-final-form-listeners-1.0.3.tgz", - "integrity": "sha512-OrdCNxSS4JQS/EXD+R530kZKFqaPfa+WcXPgVro/h4BpaBDF/Ja+BtHyCzDezCIb5rWaGGdOJIj+tN2YdtvrXg==", - "requires": { - "@babel/runtime": "^7.12.5" - } - }, - "react-i18next": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.0.0.tgz", - "integrity": "sha512-/O7N6aIEAl1FaWZBNvhdIo9itvF/MO/nRKr9pYqRc9LhuC1u21SlfwpiYQqvaeNSEW3g3qUXLREOWMt+gxrWbg==", - "requires": { - "@babel/runtime": "^7.14.5", - "html-parse-stringify": "^3.0.1" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "react-redux": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", - "integrity": "sha512-yMfQ7mX6bWuicz2fids6cR1YT59VTuT8MKyyE310wJQlINKENCeT1UcPdEiX6znI5tF8zXyJ/VYvDgeGuaaNwQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - } - }, - "react-refresh": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", - "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" - }, - "react-router": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.2.tgz", - "integrity": "sha512-Rb0BAX9KHhVzT1OKhMvCDMw776aTYM0DtkxqUBP8dNBom3mPXlfNs76JNGK8wKJ1IZEY1+WGj+cvZxHVk/GiKw==", - "requires": { - "@remix-run/router": "1.0.2" - } - }, - "react-router-dom": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.2.tgz", - "integrity": "sha512-yM1kjoTkpfjgczPrcyWrp+OuQMyB1WleICiiGfstnQYo/S8hPEEnVjr/RdmlH6yKK4Tnj1UGXFSa7uwAtmDoLQ==", - "requires": { - "@remix-run/router": "1.0.2", - "react-router": "6.4.2" - } - }, - "react-scripts": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", - "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", - "requires": { - "@babel/core": "^7.16.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", - "@svgr/webpack": "^5.5.0", - "babel-jest": "^27.4.2", - "babel-loader": "^8.2.3", - "babel-plugin-named-asset-import": "^0.3.8", - "babel-preset-react-app": "^10.0.1", - "bfj": "^7.0.2", - "browserslist": "^4.18.1", - "camelcase": "^6.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "css-loader": "^6.5.1", - "css-minimizer-webpack-plugin": "^3.2.0", - "dotenv": "^10.0.0", - "dotenv-expand": "^5.1.0", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.1", - "eslint-webpack-plugin": "^3.1.1", - "file-loader": "^6.2.0", - "fs-extra": "^10.0.0", - "fsevents": "^2.3.2", - "html-webpack-plugin": "^5.5.0", - "identity-obj-proxy": "^3.0.0", - "jest": "^27.4.3", - "jest-resolve": "^27.4.2", - "jest-watch-typeahead": "^1.0.0", - "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", - "postcss-flexbugs-fixes": "^5.0.2", - "postcss-loader": "^6.2.1", - "postcss-normalize": "^10.0.1", - "postcss-preset-env": "^7.0.1", - "prompts": "^2.4.2", - "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.1", - "react-refresh": "^0.11.0", - "resolve": "^1.20.0", - "resolve-url-loader": "^4.0.0", - "sass-loader": "^12.3.0", - "semver": "^7.3.5", - "source-map-loader": "^3.0.0", - "style-loader": "^3.3.1", - "tailwindcss": "^3.0.2", - "terser-webpack-plugin": "^5.2.5", - "webpack": "^5.64.4", - "webpack-dev-server": "^4.6.0", - "webpack-manifest-plugin": "^4.0.2", - "workbox-webpack-plugin": "^6.4.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - } - }, - "react-virtualized-auto-sizer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.7.tgz", - "integrity": "sha512-Mxi6lwOmjwIjC1X4gABXMJcKHsOo0xWl3E3ugOgufB8GJU+MqrtY35aBuvCYv/razQ1Vbp7h1gWJjGjoNN5pmA==" - }, - "react-window": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.7.tgz", - "integrity": "sha512-JHEZbPXBpKMmoNO1bNhoXOOLg/ujhL/BU4IqVU9r8eQPcy5KQnGHIHDRkJ0ns9IM5+Aq5LNwt3j8t3tIrePQzA==", - "requires": { - "@babel/runtime": "^7.0.0", - "memoize-one": ">=3.1.1 <6" - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "requires": { - "pify": "^2.3.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { - "picomatch": "^2.2.1" - } - }, - "recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "requires": { - "minimatch": "^3.0.5" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "redux": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", - "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", - "requires": { - "@babel/runtime": "^7.9.2" - } - }, - "redux-form": { - "version": "8.3.8", - "resolved": "https://registry.npmjs.org/redux-form/-/redux-form-8.3.8.tgz", - "integrity": "sha512-PzXhA0d+awIc4PkuhbDa6dCEiraMrGMyyDlYEVNX6qEyW/G2SqZXrjav5zrpXb0CCeqQSc9iqwbMtYQXbJbOAQ==", - "requires": { - "@babel/runtime": "^7.9.2", - "es6-error": "^4.1.1", - "hoist-non-react-statics": "^3.3.2", - "invariant": "^2.2.4", - "is-promise": "^2.1.0", - "lodash": "^4.17.15", - "prop-types": "^15.6.1", - "react-is": "^16.4.2" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "redux-thunk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz", - "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==" - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" - }, - "regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" - }, - "regexpu-core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.1.tgz", - "integrity": "sha512-HrnlNtpvqP1Xkb28tMhBUO2EbyUHdQlsnlAhzWcwHy8WJR53UWr7/MAvqrsQKMbV4qdpv03oTMG8iIhfsPFktQ==", - "requires": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsgen": "^0.7.1", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" - } - }, - "regjsgen": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", - "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==" - }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" - } - } - }, - "relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" - }, - "renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "requires": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "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==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "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==", - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve-url-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", - "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", - "requires": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^7.0.35", - "source-map": "0.6.1" - }, - "dependencies": { - "picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" - }, - "postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "requires": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" - }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", - "requires": { - "fsevents": "~2.3.2" - } - }, - "rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "requires": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "requires": { - "randombytes": "^2.1.0" - } - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", - "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", - "requires": { - "deepmerge": "^4.2.2", - "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", - "is-plain-object": "^5.0.0", - "parse-srcset": "^1.0.2", - "postcss": "^8.3.11" - }, - "dependencies": { - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - } - } - }, - "sanitize.css": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", - "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" - }, - "sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", - "requires": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "requires": { - "xmlchars": "^2.2.0" - } - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" - }, - "selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", - "requires": { - "node-forge": "^1" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - }, - "send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==" - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" - } - } - }, - "serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "requires": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - } - }, - "set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - } - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "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==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "shell-quote": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", - "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==" - }, - "side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - } - }, - "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==" - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" - }, - "sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "requires": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - }, - "source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" - }, - "source-map-loader": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", - "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", - "requires": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.1" - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" - }, - "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "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==" - } - } - }, - "stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" - }, - "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==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "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==" - } - } - }, - "string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", - "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", - "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.19.5" - } - }, - "stringify-object": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", - "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", - "requires": { - "get-own-enumerable-property-symbols": "^3.0.0", - "is-obj": "^1.0.1", - "is-regexp": "^1.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "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==" - }, - "strip-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", - "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" - }, - "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==" - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "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==" - }, - "style-loader": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" - }, - "stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", - "requires": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" - } - }, - "stylis": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", - "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "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==", - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "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==" - }, - "svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "dependencies": { - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - }, - "dependencies": { - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - } - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - } - } - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" - }, - "tailwindcss": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.1.tgz", - "integrity": "sha512-Uw+GVSxp5CM48krnjHObqoOwlCt5Qo6nw1jlCRwfGy68dSYb/LwS9ZFidYGRiM+w6rMawkZiu1mEMAsHYAfoLg==", - "requires": { - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "color-name": "^1.1.4", - "detective": "^5.2.1", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.2.12", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "lilconfig": "^2.0.6", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.17", - "postcss-import": "^14.1.0", - "postcss-js": "^4.0.0", - "postcss-load-config": "^3.1.4", - "postcss-nested": "6.0.0", - "postcss-selector-parser": "^6.0.10", - "postcss-value-parser": "^4.2.0", - "quick-lru": "^5.1.1", - "resolve": "^1.22.1" - }, - "dependencies": { - "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==" - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - } - } - }, - "tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==" - }, - "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" - }, - "tempy": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", - "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", - "requires": { - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "dependencies": { - "type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" - } - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.39.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz", - "integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==", - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", - "requires": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "dependencies": { - "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } - } - }, - "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==", - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "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==", - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "requires": { - "punycode": "^2.1.1" - } - }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "requires": { - "minimist": "^1.2.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - } - } - }, - "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "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==" - }, - "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==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==" - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" - }, - "update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "requires": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - } - }, - "utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" - } - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "requires": { - "makeerror": "1.0.12" - } - }, - "watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" - }, - "webpack": { - "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", - "requires": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.19.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.3" - }, - "dependencies": { - "ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - } - } - } - }, - "webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "requires": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } - } - }, - "webpack-dev-server": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", - "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", - "requires": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==" - } - } - }, - "webpack-manifest-plugin": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", - "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", - "requires": { - "tapable": "^2.0.0", - "webpack-sources": "^2.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "webpack-sources": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", - "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - } - } - } - }, - "webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==" - }, - "websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "requires": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "requires": { - "iconv-lite": "0.4.24" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "which-typed-array": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", - "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-abstract": "^1.20.0", - "for-each": "^0.3.3", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.9" - } - }, - "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==" - }, - "workbox-background-sync": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.4.tgz", - "integrity": "sha512-0r4INQZMyPky/lj4Ou98qxcThrETucOde+7mRGJl13MPJugQNKeZQOdIJe/1AchOP23cTqHcN/YVpD6r8E6I8g==", - "requires": { - "idb": "^7.0.1", - "workbox-core": "6.5.4" - } - }, - "workbox-broadcast-update": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.4.tgz", - "integrity": "sha512-I/lBERoH1u3zyBosnpPEtcAVe5lwykx9Yg1k6f8/BGEPGaMMgZrwVrqL1uA9QZ1NGGFoyE6t9i7lBjOlDhFEEw==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-build": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.4.tgz", - "integrity": "sha512-kgRevLXEYvUW9WS4XoziYqZ8Q9j/2ziJYEtTrjdz5/L/cTUa2XfyMP2i7c3p34lgqJ03+mTiz13SdFef2POwbA==", - "requires": { - "@apideck/better-ajv-errors": "^0.3.1", - "@babel/core": "^7.11.1", - "@babel/preset-env": "^7.11.0", - "@babel/runtime": "^7.11.2", - "@rollup/plugin-babel": "^5.2.0", - "@rollup/plugin-node-resolve": "^11.2.1", - "@rollup/plugin-replace": "^2.4.1", - "@surma/rollup-plugin-off-main-thread": "^2.2.3", - "ajv": "^8.6.0", - "common-tags": "^1.8.0", - "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^9.0.1", - "glob": "^7.1.6", - "lodash": "^4.17.20", - "pretty-bytes": "^5.3.0", - "rollup": "^2.43.1", - "rollup-plugin-terser": "^7.0.0", - "source-map": "^0.8.0-beta.0", - "stringify-object": "^3.3.0", - "strip-comments": "^2.0.1", - "tempy": "^0.6.0", - "upath": "^1.2.0", - "workbox-background-sync": "6.5.4", - "workbox-broadcast-update": "6.5.4", - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-google-analytics": "6.5.4", - "workbox-navigation-preload": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-range-requests": "6.5.4", - "workbox-recipes": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4", - "workbox-streams": "6.5.4", - "workbox-sw": "6.5.4", - "workbox-window": "6.5.4" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "requires": { - "whatwg-url": "^7.0.0" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "requires": { - "punycode": "^2.1.0" - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "workbox-cacheable-response": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.4.tgz", - "integrity": "sha512-DCR9uD0Fqj8oB2TSWQEm1hbFs/85hXXoayVwFKLVuIuxwJaihBsLsp4y7J9bvZbqtPJ1KlCkmYVGQKrBU4KAug==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-core": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz", - "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q==" - }, - "workbox-expiration": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.4.tgz", - "integrity": "sha512-jUP5qPOpH1nXtjGGh1fRBa1wJL2QlIb5mGpct3NzepjGG2uFFBn4iiEBiI9GUmfAFR2ApuRhDydjcRmYXddiEQ==", - "requires": { - "idb": "^7.0.1", - "workbox-core": "6.5.4" - } - }, - "workbox-google-analytics": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.4.tgz", - "integrity": "sha512-8AU1WuaXsD49249Wq0B2zn4a/vvFfHkpcFfqAFHNHwln3jK9QUYmzdkKXGIZl9wyKNP+RRX30vcgcyWMcZ9VAg==", - "requires": { - "workbox-background-sync": "6.5.4", - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "workbox-navigation-preload": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.4.tgz", - "integrity": "sha512-IIwf80eO3cr8h6XSQJF+Hxj26rg2RPFVUmJLUlM0+A2GzB4HFbQyKkrgD5y2d84g2IbJzP4B4j5dPBRzamHrng==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-precaching": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.4.tgz", - "integrity": "sha512-hSMezMsW6btKnxHB4bFy2Qfwey/8SYdGWvVIKFaUm8vJ4E53JAY+U2JwLTRD8wbLWoP6OVUdFlXsTdKu9yoLTg==", - "requires": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "workbox-range-requests": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.4.tgz", - "integrity": "sha512-Je2qR1NXCFC8xVJ/Lux6saH6IrQGhMpDrPXWZWWS8n/RD+WZfKa6dSZwU+/QksfEadJEr/NfY+aP/CXFFK5JFg==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-recipes": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.4.tgz", - "integrity": "sha512-QZNO8Ez708NNwzLNEXTG4QYSKQ1ochzEtRLGaq+mr2PyoEIC1xFW7MrWxrONUxBFOByksds9Z4//lKAX8tHyUA==", - "requires": { - "workbox-cacheable-response": "6.5.4", - "workbox-core": "6.5.4", - "workbox-expiration": "6.5.4", - "workbox-precaching": "6.5.4", - "workbox-routing": "6.5.4", - "workbox-strategies": "6.5.4" - } - }, - "workbox-routing": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz", - "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-strategies": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz", - "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==", - "requires": { - "workbox-core": "6.5.4" - } - }, - "workbox-streams": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.4.tgz", - "integrity": "sha512-FXKVh87d2RFXkliAIheBojBELIPnWbQdyDvsH3t74Cwhg0fDheL1T8BqSM86hZvC0ZESLsznSYWw+Va+KVbUzg==", - "requires": { - "workbox-core": "6.5.4", - "workbox-routing": "6.5.4" - } - }, - "workbox-sw": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.4.tgz", - "integrity": "sha512-vo2RQo7DILVRoH5LjGqw3nphavEjK4Qk+FenXeUsknKn14eCNedHOXWbmnvP4ipKhlE35pvJ4yl4YYf6YsJArA==" - }, - "workbox-webpack-plugin": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.4.tgz", - "integrity": "sha512-LmWm/zoaahe0EGmMTrSLUi+BjyR3cdGEfU3fS6PN1zKFYbqAKuQ+Oy/27e4VSXsyIwAw8+QDfk1XHNGtZu9nQg==", - "requires": { - "fast-json-stable-stringify": "^2.1.0", - "pretty-bytes": "^5.4.1", - "upath": "^1.2.0", - "webpack-sources": "^1.4.3", - "workbox-build": "6.5.4" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "workbox-window": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.4.tgz", - "integrity": "sha512-HnLZJDwYBE+hpG25AQBO8RUWBJRaCsI9ksQJEp3aCOFCaG5kqaToAYXFRAHxzRluM2cQbGzdQF5rjKPWPA1fug==", - "requires": { - "@types/trusted-types": "^2.0.2", - "workbox-core": "6.5.4" - } - }, - "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==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "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==", - "requires": { - "color-name": "~1.1.4" - } - }, - "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==" - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==" - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - }, - "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==" - } - } -} diff --git a/webclient/package.json b/webclient/package.json deleted file mode 100644 index a578fc9d9..000000000 --- a/webclient/package.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "webclient", - "version": "1.0.0", - "private": true, - "scripts": { - "prebuild": "node prebuild.js", - "prestart": "node prebuild.js", - "build": "react-scripts build", - "start": "react-scripts start", - "test": "react-scripts test", - "test:watch": "react-scripts test", - "eject": "react-scripts eject", - "lint": "eslint \"./**/*.{ts,tsx}\"", - "lint:fix": "eslint \"./**/*.{ts,tsx}\" --fix", - "golden": "npm run lint && npm run test", - "prepare": "cd .. && husky install", - "translate": "node prebuild.js -i18nOnly" - }, - "dependencies": { - "@emotion/react": "^11.8.2", - "@emotion/styled": "^11.8.1", - "@mui/icons-material": "^5.5.1", - "@mui/material": "^5.5.1", - "crypto-js": "^4.2.0", - "dexie": "^3.2.2", - "final-form": "^4.20.6", - "final-form-set-field-touched": "^1.0.1", - "i18next": "^22.0.4", - "i18next-browser-languagedetector": "^7.0.0", - "i18next-icu": "^2.0.3", - "intl-messageformat": "^10.2.1", - "lodash": "^4.17.21", - "prop-types": "^15.8.1", - "protobufjs": "^7.2.4", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-final-form": "^6.5.8", - "react-final-form-listeners": "^1.0.3", - "react-i18next": "^12.0.0", - "react-redux": "^8.0.4", - "react-router-dom": "^6.2.2", - "react-scripts": "5.0.1", - "react-virtualized-auto-sizer": "^1.0.6", - "react-window": "^1.8.6", - "redux": "^4.1.2", - "redux-form": "^8.3.8", - "redux-thunk": "^2.4.1", - "rxjs": "^7.5.4", - "sanitize-html": "^2.7.3" - }, - "devDependencies": { - "@babel/core": "^7.17.5", - "@mui/types": "^7.1.3", - "@testing-library/jest-dom": "^5.16.2", - "@testing-library/react": "^13.4.0", - "@types/jest": "29.2.0", - "@types/jquery": "^3.5.14", - "@types/lodash": "^4.14.179", - "@types/node": "18.11.7", - "@types/prop-types": "^15.7.4", - "@types/react": "18.0.24", - "@types/react-dom": "18.0.8", - "@types/react-redux": "^7.1.23", - "@types/react-router-dom": "^5.3.3", - "@types/react-virtualized-auto-sizer": "^1.0.1", - "@types/react-window": "^1.8.5", - "@types/redux-form": "^8.3.3", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", - "fs-extra": "^10.0.1", - "husky": "^8.0.1", - "typescript": "^4.6.2" - }, - "eslintConfig": { - "extends": "react-app" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "jest": { - "moduleNameMapper": { - "\\.(css|less)$": "identity-obj-proxy" - } - } -} diff --git a/webclient/prebuild.js b/webclient/prebuild.js deleted file mode 100644 index 3b420f32c..000000000 --- a/webclient/prebuild.js +++ /dev/null @@ -1,112 +0,0 @@ -const fse = require('fs-extra'); -const path = require('path'); -const util = require('util'); -const exec = util.promisify(require('child_process').exec); - -const ROOT_DIR = './src'; -const PUBLIC_DIR = './public'; - -const protoFilesDir = `${PUBLIC_DIR}/pb`; -const i18nDefaultFile = `${ROOT_DIR}/i18n-default.json`; -const serverPropsFile = `${ROOT_DIR}/server-props.json`; -const masterProtoFile = `${ROOT_DIR}/proto-files.json`; - -const sharedFiles = [ - ['../libcockatrice_protocol/libcockatrice/protocol/pb', protoFilesDir], - ['../cockatrice/resources/countries', `${ROOT_DIR}/images/countries`], -]; - -const i18nFileRegex = /\.i18n\.json$/; - -const i18nOnly = process.argv.indexOf('-i18nOnly') > -1; - -(async () => { - if (i18nOnly) { - await createI18NDefault(); - return; - } - - // make sure these files finish copying before master file is created - await copySharedFiles(); - - await createMasterProtoFile(); - await createServerProps(); - await createI18NDefault(); -})(); - -async function copySharedFiles() { - try { - return await Promise.all(sharedFiles.map(([src, dest]) => fse.copy(src, dest, { overwrite: true }))); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} - -async function createMasterProtoFile() { - try { - fse.readdir(protoFilesDir, (err, files) => { - if (err) throw err; - - fse.outputFile(masterProtoFile, JSON.stringify(files.filter(file => /\.proto$/.test(file)))); - }); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} - -async function createServerProps() { - try { - fse.outputFile(serverPropsFile, JSON.stringify({ - REACT_APP_VERSION: await getCommitHash(), - })); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} - -async function createI18NDefault() { - try { - const files = getAllFiles(ROOT_DIR, i18nFileRegex); - const allJson = await Promise.all(files.map(file => fse.readJson(file))); - - const rollup = allJson.reduce((acc, json) => { - const newKeys = Object.keys(json); - - newKeys.forEach(key => { - if (acc[key]) { - throw new Error(`i18n key collision: ${key}\n${JSON.stringify(json)}`); - } - - acc[key] = json[key]; - }); - - return acc; - }, {}); - - fse.outputFile(i18nDefaultFile, JSON.stringify(rollup, null, 2)); - } catch (e) { - console.error(e); - process.exitCode = 1; - } -} - -async function getCommitHash() { - return (await exec('git rev-parse HEAD')).stdout.trim(); -} - -function getAllFiles(dirPath, regex = /./, allFiles = []) { - return fse.readdirSync(dirPath).reduce((files, file) => { - const filePath = dirPath + "/" + file; - - if (fse.statSync(filePath).isDirectory()) { - files.concat(getAllFiles(filePath, regex, files)); - } else if (regex.test(file)) { - files.push(path.join(__dirname, filePath)); - } - - return files; - }, allFiles); -} diff --git a/webclient/public/favicon.ico b/webclient/public/favicon.ico deleted file mode 100644 index c2c86b859..000000000 Binary files a/webclient/public/favicon.ico and /dev/null differ diff --git a/webclient/public/index.html b/webclient/public/index.html deleted file mode 100644 index c050e82f4..000000000 --- a/webclient/public/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - Webatrice - - - -
- - - diff --git a/webclient/public/locales/de/translation.json b/webclient/public/locales/de/translation.json deleted file mode 100644 index 294f3fb9f..000000000 --- a/webclient/public/locales/de/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Deutsch (German)", - "disconnect": "Verbindung trennen", - "label": { - "confirmPassword": "Passwort bestätigen", - "confirmSure": "Sind Sie sicher?", - "country": "Land", - "delete": "Löschen", - "email": "E-Mail", - "hostName": "Name des Hosts", - "hostAddress": "Adresse des Hosts", - "password": "Passwort", - "passwordAgain": "Passwort Erneut", - "port": "Port", - "realName": "Richtiger Name", - "saveChanges": "Änderungen speichern", - "token": "Token", - "username": "Benutzername" - }, - "validation": { - "minChars": "Mindestens {count} {count, plural, one {Zeichen} other {Zeichen}} benötigt", - "passwordsMustMatch": "Die Passwörter stimmen nicht überein", - "required": "Benötigt" - }, - "countries": { - "AD": "Andorra", - "AE": "Vereinigte Arabische Emirate", - "AF": "Afghanistan", - "AG": "Antigua und Barbuda", - "AI": "Anguilla", - "AL": "Albanien", - "AM": "Armenien", - "AO": "Angola", - "AQ": "Antarktis", - "AR": "Argentinien", - "AS": "Amerikanisch-Samoa", - "AT": "Österreich", - "AU": "Australien", - "AW": "Aruba", - "AX": "Ålandinseln", - "AZ": "Aserbaidschan", - "BA": "Bosnien und Herzegowina", - "BB": "Barbados", - "BD": "Bangladesch", - "BE": "Belgien", - "BF": "Burkina Faso", - "BG": "Bulgarien", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint-Barthélemy ", - "BM": "Bermuda", - "BN": "Brunei", - "BO": "Bolivien", - "BQ": "Bonaire, Sint Eustatius und Saba", - "BR": "Brasilien", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvetinsel", - "BW": "Botswana", - "BY": "Weißrussland", - "BZ": "Belize", - "CA": "Kanada", - "CC": "Kokosinseln", - "CD": "DR Kongo", - "CF": "Zentralafrikanische Republik", - "CG": "Republik Kongo", - "CH": "Schweiz", - "CI": "Elfenbeinküste", - "CK": "Cookinseln", - "CL": "Chile", - "CM": "Kamerun", - "CN": "China", - "CO": "Kolumbien", - "CR": "Costa Rica", - "CU": "Kuba", - "CV": "Kap Verde", - "CW": "Curaçao", - "CX": "Weihnachtsinsel", - "CY": "Zypern", - "CZ": "Tschechien", - "DE": "Deutschland", - "DJ": "Dschibuti", - "DK": "Dänemark", - "DM": "Dominica", - "DO": "Dominikanische Republik", - "DZ": "Algerien", - "EC": "Ecuador", - "EE": "Estland", - "EG": "Ägypten", - "EH": "Westsahara", - "ER": "Eritrea", - "ES": "Spanien", - "ET": "Äthiopien", - "FI": "Finnland", - "FJ": "Fiji", - "FK": "Falklandinseln", - "FM": "Mikronesien", - "FO": "Färöer", - "FR": "Frankreich", - "GA": "Gabun", - "GB": "Vereinigtes Königreich", - "GD": "Grenada", - "GE": "Georgien", - "GF": "Französisch-Guayana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Grönland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guandeloupe", - "GQ": "Äquatorialguinea", - "GR": "Griechenland", - "GS": "Südgeorgien und die Südlichen Sandwichinseln", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hongkong", - "HM": "Heard und McDonaldinseln", - "HN": "Honduras", - "HR": "Kroatien", - "HT": "Haiti", - "HU": "Ungarn", - "ID": "Indonesien", - "IE": "Irland", - "IL": "Israel", - "IM": "Insel Man", - "IN": "Indien", - "IO": " Britisches Territorium im Indischen Ozean", - "IQ": "Irak", - "IR": "Iran", - "IS": "Island", - "IT": "Italien", - "JE": "Jersey", - "JM": "Jamaika", - "JO": "Jordanien", - "JP": "Japan", - "KE": "Kenia", - "KG": "Kirgisistan", - "KH": "Kambodscha", - "KI": "Kiribati", - "KM": "Komoren", - "KN": "St. Kitts und Nevis", - "KP": "Nordkorea", - "KR": "Südkorea", - "KW": "Kuwait", - "KY": "Kaimaninseln", - "KZ": "Kasachstan", - "LA": "Laos", - "LB": "Libanon", - "LC": "St. Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Litauen", - "LU": "Luxemburg", - "LV": "Lettland", - "LY": "Libyen", - "MA": "Marokko", - "MC": "Monaco", - "MD": "Moldau", - "ME": "Montenegro", - "MF": "Saint-Martin (Französischer Teil)", - "MG": "Madagaskar", - "MH": "Marshallinseln", - "MK": "Nordmazedonien", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolei", - "MO": "Macau", - "MP": "Nördliche Marianen", - "MQ": "Martinique", - "MR": "Mauretanien", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Malediven", - "MW": "Malawi", - "MX": "Mexiko", - "MY": "Malaysia", - "MZ": "Mosambik", - "NA": "Namibia", - "NC": "Neukaledonien", - "NE": "Niger", - "NF": "Norfolkinsel", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Niederlande", - "NO": "Norwegen", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "Neuseeland", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "Französisch-Polynesien", - "PG": "Papua-Neuguinea", - "PH": "Philippinen", - "PK": "Pakistan", - "PL": "Polen", - "PM": "St. Pierre und Miquelon", - "PN": "Pitcairninseln", - "PR": "Puerto Rico", - "PS": "Palästina", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Katar", - "RE": "Réunion", - "RO": "Rumänien", - "RS": "Serbien", - "RU": "Russland", - "RW": "Ruanda", - "SA": "Saudi-Arabien", - "SB": "Salomonen", - "SC": "Seychellen", - "SD": "Sudan", - "SE": "Schweden", - "SG": "Singapur", - "SH": "St. Helena, Ascension und Tristan da Cunha", - "SI": "Slowenien", - "SJ": "Spitzbergen und Jan Mayen", - "SK": "Slowakei", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "Südsudan", - "ST": "São Tomé und Príncipe", - "SV": "El Salvador", - "SX": "Sint Maarten (Niederländischer Teil)", - "SY": "Syrien", - "SZ": "Eswatini", - "TC": "Turks- und Caicosinseln", - "TD": "Tschad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tadschikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunesien", - "TO": "Tonga", - "TR": "Türkei", - "TT": "Trinidad und Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tansania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "Kleinere abgelegene Inseln der Vereinigten Staaten", - "US": "Vereinigte Staaten von Amerika", - "UY": "Uruguay", - "UZ": "Usbekistan", - "VA": "Heiliger Stuhl", - "VC": "St. Vincent und die Grenadinen", - "VE": "Venezuela", - "VG": "Britische Jungferninseln", - "VI": "Amerikanische Jungferninseln", - "VN": "Vietnam", - "VU": "Vanuatu", - "WF": "Wallis und Futuna", - "WS": "Samoa", - "YE": "Jemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "Südafrika", - "ZM": "Sambia", - "ZW": "Simbabwe", - "EU": "Europäische Union" - }, - "languages": { - "en-US": "Englisch - US", - "fr": "Französisch", - "nl": "Niederländisch", - "pt_BR": "Portugiesisch - Brasilien" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Neuen Host hinzufügen", - "toast": "Host erfolgreich {mode, select, created {erstellt} deleted {gelöscht} other {bearbeitet}}." - }, - "InitializeContainer": { - "title": "WUSSTEN SIE SCHON", - "subtitle": "<1>Cockatrice wird von Freiwilligen geleitet <1>die Kartenspiele lieben!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "Ein plattformübergreifender, virtueller Tabletop für Multiplayer-Kartenspiele." - }, - "footer": { - "registerPrompt": "Noch nicht registriert?", - "registerAction": "Einen Account erstellen", - "credit": "Cockatrice ist ein Open-Source-Projekt", - "version": "Version" - }, - "content": { - "subtitle1": "Spielen Sie Kartenspiele im Multiplayer online.", - "subtitle2": "Plattformübergreifender, virtueller Tabletop für Kartenspiele im Multiplayer. Für immer kostenlos." - }, - "toasts": { - "passwordResetSuccessToast": "Passwort erfolgreich zurückgesetzt", - "accountActivationSuccess": "Account erfolgreich aktiviert" - } - }, - "UnsupportedContainer": { - "title": "Browser wird nicht unterstützt", - "subtitle1": "Bitte updaten Sie Ihren Browser und/oder überprüfen Sie Ihre Berechtigungen.", - "subtitle2": "Hinweis: Beim privaten Surfen werden bei einigen Browsern bestimmte Berechtigungen oder Funktionen deaktiviert." - }, - "AccountActivationDialog": { - "title": "Aktivierung des Accounts", - "subtitle1": "Ihr Account wurde noch nicht aktiviert.", - "subtitle2": "Sie müssen das Aktivierungs-Token angeben, das Sie in der Aktivierungs-E-Mail erhalten haben." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Bearbeiten} other {Hinzufügen}} Bekannter Host", - "subtitle": "Wenn Sie einen neuen Host hinzufügen, können Sie sich mit verschiedenen Servern verbinden. Geben Sie die unten stehenden Details in Ihre Hostliste ein." - }, - "RegistrationDialog": { - "title": "Einen neuen Account erstellen" - }, - "RequestPasswordResetDialog": { - "title": "Passwort-Zurücksetzung anfordern" - }, - "ResetPasswordDialog": { - "title": "Passwort zurücksetzen" - }, - "AccountActivationForm": { - "error": { - "failed": "Account-Aktivierung fehlgeschlagen" - }, - "label": { - "activate": "Account aktivieren" - } - }, - "KnownHostForm": { - "help": "Brauchen Sie Hilfe beim Hinzufügen eines neuen Hosts?", - "label": { - "add": "Host hinzufügen", - "find": "Host finden" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Automatisch verbinden", - "forgot": "Passwort vergessen", - "login": "Login", - "savePassword": "Passwort speichern", - "savedPassword": "Passwort gespeichert" - } - }, - "RegisterForm": { - "label": { - "register": "Registrieren" - }, - "toast": { - "registerSuccess": "Registrierung erfolgreich!" - } - }, - "RequestPasswordResetForm": { - "error": "Anfrage zum Zurücksetzen des Passworts fehlgeschlagen", - "mfaEnabled": "Der Server hat Multi-Faktor-Authentifizierung aktiviert", - "request": "Rücksetzungs-Token anfordern", - "skipRequest": "Ich habe bereits ein Rücksetzungs-Token" - }, - "ResetPasswordForm": { - "error": "Passwort-Zurücksetzung fehlgeschlagen", - "label": { - "reset": "Passwort zurücksetzen" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/en_US/translation.json b/webclient/public/locales/en_US/translation.json deleted file mode 100644 index 3c63e0abb..000000000 --- a/webclient/public/locales/en_US/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "English", - "disconnect": "Disconnect", - "label": { - "confirmPassword": "Confirm Password", - "confirmSure": "Are you sure?", - "country": "Country", - "delete": "Delete", - "email": "Email", - "hostName": "Host Name", - "hostAddress": "Host Address", - "password": "Password", - "passwordAgain": "Password Again", - "port": "Port", - "realName": "Real Name", - "saveChanges": "Save Changes", - "token": "Token", - "username": "Username" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Passwords don't match", - "required": "Required" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Add new host", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "DID YOU KNOW", - "subtitle": "<1>Cockatrice is run by volunteers<1>that love card games!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "A cross-platform virtual tabletop for multiplayer card games." - }, - "footer": { - "registerPrompt": "Not registered yet?", - "registerAction": "Create an account", - "credit": "Cockatrice is an open source project", - "version": "Version" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - }, - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - }, - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - }, - "RegistrationDialog": { - "title": "Create New Account" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "Reset Password" - }, - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - }, - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Login", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Reset Password" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/es/translation.json b/webclient/public/locales/es/translation.json deleted file mode 100644 index 33a3db45c..000000000 --- a/webclient/public/locales/es/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Español (Spanish)", - "disconnect": "Desconectar", - "label": { - "confirmPassword": "Confirmar contraseña", - "confirmSure": "¿Estás seguro?", - "country": "País", - "delete": "Borrar", - "email": "e-mail", - "hostName": "Nombre del servidor", - "hostAddress": "Dirección del servidor", - "password": "Contraseña", - "passwordAgain": "Contraseña de nuevo", - "port": "Puerto", - "realName": "Nombre real", - "saveChanges": "Guardar cambios", - "token": "Ficha", - "username": "Nombre de usuario" - }, - "validation": { - "minChars": "Se requiere un mínimo de {conteo} {conteo, plural, un {carácter} otros {caracteres}}", - "passwordsMustMatch": "Las contraseñas no coinciden", - "required": "Requerido" - }, - "countries": { - "AD": "Andorra", - "AE": "Emiratos Árabes Unidos", - "AF": "Afganistán", - "AG": "Antigua y Barbuda", - "AI": "Anguila", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antártida", - "AR": "Argentina", - "AS": "Samoa americana", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Islas Aland", - "AZ": "Azerbaiján", - "BA": "Bosnia y Herzegovina", - "BB": "Barbados", - "BD": "Bangladés", - "BE": "Bélgica", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Baréin", - "BI": "Burundi", - "BJ": "Benín", - "BL": "San Bartolomé", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, San Eustaquio y Saba", - "BR": "Brasil", - "BS": "Bahamas", - "BT": "Bután", - "BV": "Isla Bouvet", - "BW": "Botswana", - "BY": "Belorrusia", - "BZ": "Belice", - "CA": "Canadá", - "CC": "Islas (Keeling) Cocos", - "CD": "República Democrática del Congo", - "CF": "República Centroafricana", - "CG": "República del Congo", - "CH": "Suiza", - "CI": "Costa de Marfil", - "CK": "Islas Cook", - "CL": "Chile", - "CM": "Camerún", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cabo Verde", - "CW": "Curazao", - "CX": "Isla de Navidad", - "CY": "Chipre", - "CZ": "República Checa", - "DE": "Alemania", - "DJ": "Djibouti", - "DK": "Dinamarca", - "DM": "Dominica", - "DO": "República Dominicana", - "DZ": "Argelia", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egipto", - "EH": "Sáhara Occidental", - "ER": "Eritrea", - "ES": "España", - "ET": "Etiopía", - "FI": "Finlandia", - "FJ": "Fiji", - "FK": "Islas Falkland", - "FM": "Micronesia", - "FO": "Islas Faroe", - "FR": "Francia", - "GA": "Gabón", - "GB": "Reino Unido", - "GD": "Granada", - "GE": "Georgia", - "GF": "Guinea Francesa", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambía", - "GN": "Guinea", - "GP": "Guadalupe", - "GQ": "Guinea Ecuatorial", - "GR": "Grecia", - "GS": "Georgia del Sur y las islas Sandwich del sur", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bisáu", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Islas Heard y McDonald", - "HN": "Honduras", - "HR": "Croacia", - "HT": "Haití", - "HU": "Hungría", - "ID": "Indonesia", - "IE": "Irlanda", - "IL": "Israel", - "IM": "Isla de Man", - "IN": "India", - "IO": "Territorio Británico del Océano Índico", - "IQ": "Irak", - "IR": "Irán", - "IS": "Groenlandia", - "IT": "Italia", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordania", - "JP": "Japón", - "KE": "Kenya", - "KG": "Kirguistán", - "KH": "Camboya", - "KI": "Kiribati", - "KM": "Comoras", - "KN": "San Cristóbal y Nieves", - "KP": "Corea la Buena-Norte", - "KR": "Corea la Mala-Sur", - "KW": "Kuwait", - "KY": "Islas Caimán", - "KZ": "Kazajistán", - "LA": "Laos", - "LB": "Líbano", - "LC": "Santa Lucía", - "LI": "Principado de Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesoto", - "LT": "Lituania", - "LU": "Luxemburgo", - "LV": "Letonia", - "LY": "Libia", - "MA": "Marruecos", - "MC": "Mónaci", - "MD": "Moldavia", - "ME": "Montenegro", - "MF": "San Martín (Parte francesa)", - "MG": "Madagascar", - "MH": "Islas Marshall", - "MK": "Macedonia del Norte", - "ML": "Malí", - "MM": "Birmania", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Islas Marianas del Norte", - "MQ": "Martinica", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauricio", - "MV": "Maldivas", - "MW": "Malawi", - "MX": "México", - "MY": "Malasia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "Nueva Caledonia", - "NE": "Nigeria", - "NF": "Isla Norfolk", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Países Bajos (Holanda)", - "NO": "Noruega", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "Nueva Zelanda", - "OM": "Omán", - "PA": "Panamá", - "PE": "Perú", - "PF": "Polinesia Francesa", - "PG": "Papúa Nueva Guinea", - "PH": "Filipinas", - "PK": "Pakistán", - "PL": "Polonia", - "PM": "San Pedro y Miquelón", - "PN": "Islas Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestina", - "PT": "Portugal", - "PW": "Palaos", - "PY": "Paraguay", - "QA": "Catar", - "RE": "Reunión", - "RO": "Rumanía", - "RS": "Serbia", - "RU": "Rusia", - "RW": "Ruanda", - "SA": "Arabia Saudí", - "SB": "Islas Salomón", - "SC": "Islas Seychelles", - "SD": "Sudán", - "SE": "Suecia", - "SG": "Singapur", - "SH": "Santa Elena, Ascensión y Tristán de Acuña", - "SI": "Eslovenia", - "SJ": "Svalbard y Jan Mayen", - "SK": "Eslovaquia", - "SL": "Sierra Leona", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Surinam", - "SS": "Sudán del Sur", - "ST": "Santo Tomé y Príncipe", - "SV": "El Salvador", - "SX": "Sint Maarten (Países Bajos-Holanda)", - "SY": "Siria", - "SZ": "Esuatini", - "TC": "Islas Turcas y Caicos", - "TD": "Chad", - "TF": "Las Tierras Australes y Antárticas Francesas (TAAF)", - "TG": "Togo", - "TH": "Tailandia", - "TJ": "Tayikistán", - "TK": "Tokelau", - "TL": "Timor Oriental", - "TM": "Turkmenistán", - "TN": "Túnez", - "TO": "Tonga", - "TR": "Turquía", - "TT": "Trinidad y Tobago", - "TV": "Tuvalu", - "TW": "Taiwán", - "TZ": "Tanzania", - "UA": "Ucrania", - "UG": "Uganda", - "UM": "Islas Ultramarinas Menores de Estados Unidos", - "US": "Estados Unidos", - "UY": "Uruguay", - "UZ": "Uzbekistán", - "VA": "Ciudad del Vaticano", - "VC": "San Vicente y las Granadinas", - "VE": "Venezuela", - "VG": "Islas Vírgenes Británicas", - "VI": "Islas Vírgenes Estadounidenses", - "VN": "Vietnam", - "VU": "Vanuatu", - "WF": "Wallis y Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kósovo", - "ZA": "Sudáfrica", - "ZM": "Zambia", - "ZW": "Zimbabue", - "EU": "Unión Europea" - }, - "languages": { - "en-US": "Inglés - US", - "fr": "Francés", - "nl": "Holandés", - "pt_BR": " Portugués - Brasil" - } - }, - "KnownHosts": { - "label": "IP", - "add": "Añadir nueva dirección IP", - "toast": "Dirección IP correcta {modo, seleccionar, creado {creado} eliminado {eliminado} otro {editado}}." - }, - "InitializeContainer": { - "title": "¿LO SABÍAS?", - "subtitle": "<1>¡Cockatrice está buscando voluntarios<1> que amen los juegos de cartas!" - }, - "LoginContainer": { - "header": { - "title": "Conectar", - "subtitle": "Un tablero de mesa multiplataforma para juegos de cartas multijugador." - }, - "footer": { - "registerPrompt": "¿No estás registrado aún?", - "registerAction": "Crear una cuenta", - "credit": "Cockatrice es un proyecto de fuente abierta", - "version": "Versión" - }, - "content": { - "subtitle1": "Juega juegos de cartas multijugador en línea.", - "subtitle2": "Tablero multiplataforma virtual para juegos de cartas multijugador. Siempre gratuito." - }, - "toasts": { - "passwordResetSuccessToast": "Reinicio de contraseña exitoso", - "accountActivationSuccess": "Cuenta activada con éxito" - } - }, - "UnsupportedContainer": { - "title": "Navegador sin apoyo", - "subtitle1": "Por favor actualiza tu navegador y/o comprueba tus permisos.", - "subtitle2": "Nota: Los navegadores privados pueden causar que los navegadores deshabiliten ciertos permisos o funciones." - }, - "AccountActivationDialog": { - "title": "Activación de la cuenta", - "subtitle1": "Tu cuenta aún no ha sido activada.", - "subtitle2": "Necesitas comprobar el mensaje de activación recibido en el e-mail de activación." - }, - "KnownHostDialog": { - "title": "{modo, seleccionar, editar {Editar} otro {Agregar}} IP conocida", - "subtitle": "Añadir una nueva dirección IP te permite conectarte a diferentes servidores. Introduce los detalles a continuación a tu lista de IPs." - }, - "RegistrationDialog": { - "title": "Crear nueva cuenta" - }, - "RequestPasswordResetDialog": { - "title": "Requiere reinicio de contraseña" - }, - "ResetPasswordDialog": { - "title": "Reiniciar contraseña" - }, - "AccountActivationForm": { - "error": { - "failed": "La activación de la cuenta falló" - }, - "label": { - "activate": "Activar cuenta" - } - }, - "KnownHostForm": { - "help": "¿Necesitas ayuda al añadir una nueva dirección IP?", - "label": { - "add": "Añadir dirección IP", - "find": "Encontrar dirección IP" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Conectarse automáticamente", - "forgot": "Contraseña olvidada", - "login": "Conectar", - "savePassword": "Guardar contraseña", - "savedPassword": "Contraseña guardada" - } - }, - "RegisterForm": { - "label": { - "register": "Registrarse" - }, - "toast": { - "registerSuccess": "¡Te has registrado correctamente!" - } - }, - "RequestPasswordResetForm": { - "error": "Solicitud de restablecimiento de contraseña fallida", - "mfaEnabled": "El servidor tiene habilitada la autenticación multifactor", - "request": "Se requiere reiniciar el token", - "skipRequest": "Ya tengo un reinicio de token" - }, - "ResetPasswordForm": { - "error": "El reinicio de la contraseña falló", - "label": { - "reset": "Reiniciar contraseña" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/fi/translation.json b/webclient/public/locales/fi/translation.json deleted file mode 100644 index 12737731a..000000000 --- a/webclient/public/locales/fi/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Suomi (Finnish)", - "disconnect": "Katkaise yhteys", - "label": { - "confirmPassword": "Vahvista salasana", - "confirmSure": "Oletko varma?", - "country": "Maa", - "delete": "Poista", - "email": "Sähköposti", - "hostName": "Isännän Nimi", - "hostAddress": "Isännän Osoite", - "password": "Salasana", - "passwordAgain": "Salasana uudelleen", - "port": "Portti", - "realName": "Oikea nimi", - "saveChanges": "Tallenna muutokset", - "token": "Tokeni", - "username": "Käyttäjänimi" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Passwords don't match", - "required": "Required" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Isäntä", - "add": "Lisää isäntä", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "TIESITKÖ", - "subtitle": "<1>Cockatricea pyörittävät vapaaehtoiset<1>jotka rakastavat korttipelejä!" - }, - "LoginContainer": { - "header": { - "title": "Kirjaudu sisään", - "subtitle": "Järjestelmäriippumaton virtuaalinen pöytä moninpelikorttipelejä varten." - }, - "footer": { - "registerPrompt": "Etkö ole vielä rekisteröitynyt?", - "registerAction": "Luo tili", - "credit": "Cockatrice on avoimen lähdekoodin projekti", - "version": "Versio" - }, - "content": { - "subtitle1": "Pelaa moninpelikorttipelejä verkossa.", - "subtitle2": "Järjestelmäriippumaton virtuaalinen pöytä moninpelikorttipelejä varten. Ikuisesti ilmainen." - }, - "toasts": { - "passwordResetSuccessToast": "Salasanan Nollaus Onnistui", - "accountActivationSuccess": "Tilin Aktivointi Onnistui" - } - }, - "UnsupportedContainer": { - "title": "Selainta ei tueta", - "subtitle1": "Päivitä selain ja/tai tarksita käyttöoikeudet.", - "subtitle2": "Huomautus: Yksityinen selaaminen saa jotkin selaimet poistamaan tietyt käyttöoikeudet tai ominaisuudet käytöstä." - }, - "AccountActivationDialog": { - "title": "Tilin Aktivointi", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Uuden isännän lisääminen mahdollistaa yhdistämisen eri palvelimiin. Lisää alla olevat tiedot isäntäluetteloosi." - }, - "RegistrationDialog": { - "title": "Luo Tili" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "Nollaa Salasana" - }, - "AccountActivationForm": { - "error": { - "failed": "Tilin aktivointi epäonnistui" - }, - "label": { - "activate": "Aktivoi Tili" - } - }, - "KnownHostForm": { - "help": "Tarvitsetko apua isännän lisäämisessä?", - "label": { - "add": "Lisää Isäntä", - "find": "Etsi Isäntä" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Kirjaudu sisään", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Nollaa Salasana" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/fr/translation.json b/webclient/public/locales/fr/translation.json deleted file mode 100644 index aacd26d80..000000000 --- a/webclient/public/locales/fr/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Français (French)", - "disconnect": "Déconnecter", - "label": { - "confirmPassword": "Confirmer le mot de passe", - "confirmSure": "Êtes-vous sûr ?", - "country": "Pays", - "delete": "Effacer", - "email": "E-mail", - "hostName": "Nom d'hôte", - "hostAddress": "Adresse d'hôte", - "password": "Mot de passe", - "passwordAgain": "Mot de passe à nouveau", - "port": "Port", - "realName": "Nom réel", - "saveChanges": "Sauvegarder les changements", - "token": "Jeton", - "username": "Nom d'utilisateur" - }, - "validation": { - "minChars": "Minimum de {count} {count, plural, one {caractère} other {caractères}} requis", - "passwordsMustMatch": "Les mots de passe ne correspondent pas.", - "required": "Requis" - }, - "countries": { - "AD": "Andorre", - "AE": "Émirats arabes unis", - "AF": "Afghanistan", - "AG": "Antigua-et-Barbuda", - "AI": "Anguilla", - "AL": "Albanie", - "AM": "Arménie", - "AO": "Angola", - "AQ": "Antarctique", - "AR": "Argentine", - "AS": "Samoa américaines", - "AT": "Autriche", - "AU": "Australie", - "AW": "Aruba", - "AX": "Åland", - "AZ": "Azerbaijan", - "BA": "Bosnie-Herzégovine", - "BB": "Barbade", - "BD": "Bangladesh", - "BE": "Belgique", - "BF": "Burkina Faso", - "BG": "Bulgarie", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Bénin", - "BL": "Saint Barthélemy", - "BM": "Bermudes", - "BN": "Brunéi Darussalam", - "BO": "Bolivie", - "BQ": "Pays-Bas caribéens", - "BR": "Brésil", - "BS": "Bahamas", - "BT": "Bhoutan", - "BV": "Île Bouvet", - "BW": "Botswana", - "BY": "Biélorussie", - "BZ": "Belize", - "CA": "Canada", - "CC": "Îles Cocos (Keeling)", - "CD": "République Démocratique du Congo", - "CF": "République Centrafricaine", - "CG": "République du Congo", - "CH": "Suisse", - "CI": "Côte d'Ivoire", - "CK": "Îles Cook", - "CL": "Chili", - "CM": "Cameroun", - "CN": "Chine", - "CO": "Colombie", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cap-Vert", - "CW": "Curaçao", - "CX": "Île Christmas", - "CY": "Chypre", - "CZ": "Tchéquie", - "DE": "Allemagne", - "DJ": "Djibouti", - "DK": "Danemark", - "DM": "Dominique", - "DO": "République dominicaine", - "DZ": "Algérie", - "EC": "Équateur", - "EE": "Estonie", - "EG": "Égypte", - "EH": "Sahara Occidental", - "ER": "Érythrée", - "ES": "Espagne", - "ET": "Éthiopie", - "FI": "Finlande", - "FJ": "Fidji", - "FK": "Îles Falkland", - "FM": "Micronésie", - "FO": "Îles Féroé", - "FR": "France", - "GA": "Gabon", - "GB": "Royaume-Uni", - "GD": "Grenade", - "GE": "Géorgie", - "GF": "Guyane Française", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Groenland", - "GM": "Gambie", - "GN": "Guinée", - "GP": "Guadeloupe", - "GQ": "Guinée équatoriale", - "GR": "Grèce", - "GS": "Géorgie du Sud et les Îles Sandwich du Sud", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinée-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Îles Heard et McDonald", - "HN": "Honduras", - "HR": "Croatie", - "HT": "Haïti", - "HU": "Hongrie", - "ID": "Indonésie", - "IE": "Irlande", - "IL": "Israël", - "IM": "Île de Man", - "IN": "Inde", - "IO": "Territoire britannique de l'océan Indien", - "IQ": "Irak", - "IR": "Iran", - "IS": "Islande", - "IT": "Italie", - "JE": "Jersey", - "JM": "Jamaïque", - "JO": "Jordanie", - "JP": "Japon", - "KE": "Kenya", - "KG": "Kirghizistan", - "KH": "Cambodge", - "KI": "Kiribati", - "KM": "Comores", - "KN": "Saint-Kitts-et-Nevis", - "KP": "République Populaire et Démocratique de Corée", - "KR": "Corée du Sud", - "KW": "Koweït", - "KY": "Îles Caïmans", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Liban", - "LC": "Sainte-Lucie", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lituanie", - "LU": "Luxembourg", - "LV": "Lettonie", - "LY": "Lybie", - "MA": "Maroc", - "MC": "Monaco", - "MD": "Moldavie", - "ME": "Montenegro", - "MF": "Saint-Martin (Territoire Français)", - "MG": "Madagascar", - "MH": "Îles Marshall", - "MK": "Macédoine du Nord", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolie", - "MO": "Macao", - "MP": "Îles Mariannes du Nord", - "MQ": "Martinique", - "MR": "Mauritanie", - "MS": "Montserrat", - "MT": "Malte", - "MU": "Maurice", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexique", - "MY": "Malaisie", - "MZ": "Mozambique", - "NA": "Namibie", - "NC": "Nouvelle Calédonie", - "NE": "Niger", - "NF": "Île Norfolk", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Pays-Bas", - "NO": "Norvège", - "NP": "Népal", - "NR": "Nauru", - "NU": "Niué", - "NZ": "Nouvelle-Zélande", - "OM": "Oman", - "PA": "Panama", - "PE": "Pérou", - "PF": "Polynésie Française", - "PG": "Papouasie-Nouvelle-Guinée", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Pologne", - "PM": "Saint Pierre et Miquelon", - "PN": "Îles Pitcairn", - "PR": "Porto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palaos", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "La Réunion", - "RO": "Roumanie", - "RS": "Serbie", - "RU": "Russie", - "RW": "Rwanda", - "SA": "Arabie Saoudite", - "SB": "Îles Salomon", - "SC": "Seychelles", - "SD": "Soudan", - "SE": "Suède", - "SG": "Singapour", - "SH": "Sainte-Hélène", - "SI": "Slovénie", - "SJ": "Svalbard et Jan Mayen", - "SK": "Slovaquie", - "SL": "Sierra Leone", - "SM": "Saint-Marin", - "SN": "Sénégal", - "SO": "Somalie", - "SR": "Suriname", - "SS": "Soudan du Sud", - "ST": "Sao Tome et Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (territoire des Pays-Bas)", - "SY": "Syrie", - "SZ": "Eswatini", - "TC": "Îles Turks et Caïques", - "TD": "Tchad", - "TF": "Terres australes et antarctiques françaises", - "TG": "Togo", - "TH": "Thailande", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisie", - "TO": "Tonga", - "TR": "Turquie", - "TT": "Trinité-et-Tobago", - "TV": "Tuvalu", - "TW": "Taïwan", - "TZ": "Tanzanie", - "UA": "Ukraine", - "UG": "Ouganda", - "UM": "Îles mineures éloignées des États-Unis", - "US": "États-Unis", - "UY": "Uruguay", - "UZ": "Ouzbekistan", - "VA": "Saint-Siège", - "VC": "Saint-Vincent et les Grenadines", - "VE": "Venezuela", - "VG": "Îles Vierges britanniques", - "VI": "Îles Vierges des États-Unis", - "VN": "Vietnam", - "VU": "Vanuatu", - "WF": "Wallis et Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "Afrique du Sud", - "ZM": "Zambie", - "ZW": "Zimbabwe", - "EU": "Union Européenne" - }, - "languages": { - "en-US": "Anglais - US", - "fr": "Français", - "nl": "Néerlandais", - "pt_BR": "Portugais - Brésil" - } - }, - "KnownHosts": { - "label": "Hôte", - "add": "Ajouter un nouvel hôte", - "toast": "Hôte {mode, select, created {créé} deleted {effacé} other {édité}} avec succès." - }, - "InitializeContainer": { - "title": "LE SAVIEZ-VOUS ?", - "subtitle": "<1>Cockatrice est géré par des volontaires<1>qui aiment les jeux de cartes !" - }, - "LoginContainer": { - "header": { - "title": "S'identifier", - "subtitle": "Un jeu de table virtuel multi-plate-forme pour jeux de cartes multijoueurs." - }, - "footer": { - "registerPrompt": "Pas encore inscrit ?", - "registerAction": "Créer un compte", - "credit": "Cockatrice est un projet open source.", - "version": "Version" - }, - "content": { - "subtitle1": "Jouer à des jeux de cartes multijoueurs en ligne.", - "subtitle2": "Jeu de table virtuel multi-plate-forme pour jeux de cartes multijoueurs. Gratuit pour toujours." - }, - "toasts": { - "passwordResetSuccessToast": "Mot de passe réinitialisé avec succès", - "accountActivationSuccess": "Compte activé avec succès" - } - }, - "UnsupportedContainer": { - "title": "Navigateur non supporté", - "subtitle1": "Veuillez mettre à jour votre navigateur et/ou vérifier vos permissions.", - "subtitle2": "Note : La navigation privée désactive certaines permissions ou fonctionnalités." - }, - "AccountActivationDialog": { - "title": "Activation du compte", - "subtitle1": "Votre compte n'a pas encore été activé.", - "subtitle2": "Vous devez fournir le jeton d'activation reçu dans l'e-mail d'activation." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Éditer} other {Ajouter}} un hôte connu", - "subtitle": "Ajouter un nouvel hôte vous permet de vous connecter à différents serveurs. Entrez les détails ci-dessous dans votre liste d'hôtes." - }, - "RegistrationDialog": { - "title": "Créer un nouveau compte" - }, - "RequestPasswordResetDialog": { - "title": "Demander une réinitialisation du mot de passe" - }, - "ResetPasswordDialog": { - "title": "Réinitialiser le mot de passe" - }, - "AccountActivationForm": { - "error": { - "failed": "L'activation du compte a échoué." - }, - "label": { - "activate": "Activer le compte" - } - }, - "KnownHostForm": { - "help": "Besoin d'aide pour ajouter un nouvel hôte ?", - "label": { - "add": "Ajouter un hôte", - "find": "Trouver un hôte" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Connexion automatique", - "forgot": "Mot de passe oublié", - "login": "S'identifier", - "savePassword": "Enregistrer le mot de passe", - "savedPassword": "Mot de passe enregistré" - } - }, - "RegisterForm": { - "label": { - "register": "S'enregistrer" - }, - "toast": { - "registerSuccess": "Enregistrement réussi !" - } - }, - "RequestPasswordResetForm": { - "error": "La demande de réinitialisation du mot de passe a échoué.", - "mfaEnabled": "L'authentification multifacteur est activée sur ce serveur.", - "request": "Réinitialiser jeton", - "skipRequest": "Je possède déjà un jeton de réinitialisation." - }, - "ResetPasswordForm": { - "error": "La réinitialisation du mot de passe a échoué.", - "label": { - "reset": "Réinitialiser le mot de passe" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/it/translation.json b/webclient/public/locales/it/translation.json deleted file mode 100644 index 74734384b..000000000 --- a/webclient/public/locales/it/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Italiano (Italian)", - "disconnect": "Disconnetti", - "label": { - "confirmPassword": "Conferma Password", - "confirmSure": "Sei sicuro?", - "country": "Stato", - "delete": "Elimina", - "email": "Email", - "hostName": "Nome Host", - "hostAddress": "Indirizzo Host", - "password": "Password", - "passwordAgain": "Ripeti Password", - "port": "Porta", - "realName": "Nome reale", - "saveChanges": "Salva Cambiamenti", - "token": "Pedina", - "username": "Nome utente" - }, - "validation": { - "minChars": "Minimo di {%n} carattere(i) richiesti", - "passwordsMustMatch": "Le password non coincidono.", - "required": "Richiesto" - }, - "countries": { - "AD": "Andorra", - "AE": "Emirati Arabi Uniti", - "AF": "Afghanistan", - "AG": "Antigua e Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antartide", - "AR": "Argentina", - "AS": "Samoa Americane", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Isole Åland", - "AZ": "Azerbaijan", - "BA": "Bosnia ed Erzegovina", - "BB": "Barbados", - "BD": "Bangledesh", - "BE": "Belgio", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrein", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei", - "BO": "Bolivia", - "BQ": "Paesi Bassi Caraibici", - "BR": "Brasile", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Isola Bouvet", - "BW": "Botswana", - "BY": "Bielorussia", - "BZ": "Belize", - "CA": "Canada", - "CC": "Isole Cocos (Keeling)", - "CD": "DR Congo", - "CF": "Repubblica Centrafricana", - "CG": "Repubblica del Congo", - "CH": "Svizzera", - "CI": "Costa D'Avorio", - "CK": "Isole Cook", - "CL": "Cile", - "CM": "Camerun", - "CN": "Cina", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Capo Verde", - "CW": "Curaçao", - "CX": "Isola Christmas", - "CY": "Cipro", - "CZ": "Repubblica Ceca", - "DE": "Germania", - "DJ": "Gibuti", - "DK": "Danimarca", - "DM": "Dominica", - "DO": "Repubblica Domenicana", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egitto", - "EH": "Sahara Occidentale", - "ER": "Eritrea", - "ES": "Spagna", - "ET": "Etiopia", - "FI": "Finlandia", - "FJ": "Fiji", - "FK": "Isole Falkland", - "FM": "Micronesia", - "FO": "Isole Faroe", - "FR": "Francia", - "GA": "Gabon", - "GB": "Regno Unito", - "GD": "Grenada", - "GE": "Georgia", - "GF": "Guyana Francese", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Groenlandia", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadalupa", - "GQ": "Guinea Equatoriale", - "GR": "Grecia", - "GS": "Georgia del Sud e Isole Sandwich Australi", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Isole Heard e McDonald", - "HN": "Honduras", - "HR": "Croazia", - "HT": "Haiti", - "HU": "Ungheria", - "ID": "Indonesia", - "IE": "Irlanda", - "IL": "Israele", - "IM": "Isola di Man", - "IN": "India", - "IO": "Territorio britannico dell'Oceano Indiano", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Islanda", - "IT": "Italia", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Giordania", - "JP": "Giappone", - "KE": "Kenya", - "KG": "Kirghizistan", - "KH": "Cambogia", - "KI": "Kiribati", - "KM": "Comore", - "KN": "Saint Kitts e Nevis", - "KP": "Korea del Nord", - "KR": "Korea del Sud", - "KW": "Kuwait", - "KY": "Isole Cayman", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebano", - "LC": "Santa Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lituania", - "LU": "Lussemburgo", - "LV": "Lettonia", - "LY": "Libia", - "MA": "Marocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "San Martino (parte Francese)", - "MG": "Madagascar", - "MH": "Isole Marshall", - "MK": "Macedonia del Nord", - "ML": "Mali", - "MM": "Birmania", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Isole Marianne Settentrionali", - "MQ": "Martinica", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldive", - "MW": "Malawi", - "MX": "Messico", - "MY": "Malesia", - "MZ": "Mozambico", - "NA": "Namibia", - "NC": "Nuova Caledonia", - "NE": "Niger", - "NF": "Isola Norfolk", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Paesi Bassi", - "NO": "Norvegia", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "Nuova Zelanda", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "Polinesia Francese", - "PG": "Papua Nuova Guinea", - "PH": "Filippine", - "PK": "Pakistan", - "PL": "Polonia", - "PM": "Saint Pierre e Miquelon", - "PN": "Pitcairn", - "PR": "Porto Rico", - "PS": "Palestina", - "PT": "Portogallo", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "La Riunione", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Arabia Saudita", - "SB": "Isole Salomone", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Svezia", - "SG": "Singapore", - "SH": "Sant'Elena, Ascensione e Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard e Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "Sud Sudan", - "ST": "Sao Tome e Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Parte Olandese)", - "SY": "Siria", - "SZ": "eSwatini", - "TC": "Turks e Caicos", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor Est", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turchia", - "TT": "Trinidad e Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ucraina", - "UG": "Uganda", - "UM": "Isole minori esterne degli Stati Uniti d'America", - "US": "Stati Uniti", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Vaticano", - "VC": "Saint Vincent e Grenadine", - "VE": "Venezuela", - "VG": "Isole Vergini britanniche", - "VI": "Isole Vergini americane", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis e Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "Sud Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "Unione Europea" - }, - "languages": { - "en-US": "Inglese - US", - "fr": "Francese", - "nl": "Olandese", - "pt_BR": "Portoghese - Brasile" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Aggiungi nuovo Host", - "toast": "Host {mode, select, created {creato} deleted {eliminato} other {modificato}} con successo." - }, - "InitializeContainer": { - "title": "LO SAPEVI", - "subtitle": "<1>Cockatrice è gestita da volontari<1>che amano i giochi di carte!" - }, - "LoginContainer": { - "header": { - "title": "Accesso", - "subtitle": "Un portale digitale multi-piattaforma per giochi di carte multigiocatore" - }, - "footer": { - "registerPrompt": "Non sei ancora registrato?", - "registerAction": "Crea un nuovo account", - "credit": "Cockatrice è un progetto open source", - "version": "Versione" - }, - "content": { - "subtitle1": "Gioca online giochi di carte multigiocatore.", - "subtitle2": "Portale digitale multi-piattaforma per giochi di carte multigiocatore. Sempre gratis." - }, - "toasts": { - "passwordResetSuccessToast": "Password reimpostata con successo", - "accountActivationSuccess": "Account attivato con successo" - } - }, - "UnsupportedContainer": { - "title": "Browser non supportato", - "subtitle1": "Aggiornare il browser e/o controllare i permessi.", - "subtitle2": "Nota: la navigazione Privata può far si che il browser disabiliti certi permessi o funzionalità." - }, - "AccountActivationDialog": { - "title": "Attivazione dell'account", - "subtitle1": "Il tuo account non è ancora stato attivato.", - "subtitle2": "È necessario fornire il codice ricevuto nell'e-mail di attivazione." - }, - "KnownHostDialog": { - "title": "{modalità, seleziona, modifica {Modifica} altro {Aggiungi}} Host conosciuto", - "subtitle": "L'aggiunta di un nuovo host consente di connettersi a server diversi. Inserisci i dettagli di seguito nella lista degli host." - }, - "RegistrationDialog": { - "title": "Crea un nuovo account" - }, - "RequestPasswordResetDialog": { - "title": "Richiedi la reimpostazione della password" - }, - "ResetPasswordDialog": { - "title": "Reimposta Password" - }, - "AccountActivationForm": { - "error": { - "failed": "Attivazione dell'account non riuscita" - }, - "label": { - "activate": "Attiva account" - } - }, - "KnownHostForm": { - "help": "Hai bisogno di aiuto per aggiungere un nuovo host?", - "label": { - "add": "Aggiungi Host", - "find": "Trova Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Connessione automatica", - "forgot": "Password dimenticata", - "login": "Accesso", - "savePassword": "Salva Password", - "savedPassword": "Password salvata" - } - }, - "RegisterForm": { - "label": { - "register": "Iscriviti" - }, - "toast": { - "registerSuccess": "Iscrizione completata correttamente!" - } - }, - "RequestPasswordResetForm": { - "error": "Richiesta reimpostazione password non riuscita", - "mfaEnabled": "Il server ha l'autenticazione a più fattori abilitata", - "request": "Richiedi il token di ripristino", - "skipRequest": "Ho già un token di ripristino" - }, - "ResetPasswordForm": { - "error": "Reimpostazione della password non riuscita", - "label": { - "reset": "Reimposta Password" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/nl/translation.json b/webclient/public/locales/nl/translation.json deleted file mode 100644 index fffb18989..000000000 --- a/webclient/public/locales/nl/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Nederlands (Dutch)", - "disconnect": "Verbinding Verbreken", - "label": { - "confirmPassword": "Wachtwoord Bevestigen", - "confirmSure": "Weet je het zeker?", - "country": "Land", - "delete": "Verwijderen", - "email": "Email", - "hostName": "Hostnaam", - "hostAddress": "Hostaddres", - "password": "Wachtwoord", - "passwordAgain": "Wachtwoord Nogmaals", - "port": "Poort", - "realName": "Echte Naam", - "saveChanges": "Wijzigingen Opslaan", - "token": "Token", - "username": "Gebruikersnaam" - }, - "validation": { - "minChars": "Minimum van {count} {count, plural, one {karakter} other {karakters}} verplicht", - "passwordsMustMatch": "Wachtwoorden komen niet overeen", - "required": "Verplicht" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Voeg nieuwe host toe", - "toast": "Host {mode, select created {toegevoegd} deleted {verwijderd} other {aangepast}}." - }, - "InitializeContainer": { - "title": "WIST JE DAT", - "subtitle": "<1>Cockatrice word gerund door vrijwilligers<1>die van kaartspellen houden!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "Een cross-platform virtual tabletop voor multiplayer kaartspellen." - }, - "footer": { - "registerPrompt": "Nog niet geregistreerd?", - "registerAction": "Maak een account aan", - "credit": "Cockatrice is een open source project", - "version": "Versie" - }, - "content": { - "subtitle1": "Speel multiplayer kaartspellen online.", - "subtitle2": "Cross-platform virtual tabletop voor multiplayer kaartspellen. Gratis en open source." - }, - "toasts": { - "passwordResetSuccessToast": "Wachtwoord Opnieuw Ingesteld", - "accountActivationSuccess": "Account Succesvol Geactiveerd" - } - }, - "UnsupportedContainer": { - "title": "Niet-ondersteunde Browser", - "subtitle1": "Update je browser en/of check je instellingen.", - "subtitle2": "Let op: Private browsing zorgt er voor dat sommige browsers bepaalde toestemmingen of functies uitschakelen." - }, - "AccountActivationDialog": { - "title": "Account Activatie", - "subtitle1": "Je account is nog niet geactiveerd.", - "subtitle2": "Vul de activerings-token die je hebt ontvangen via de activerings-email in." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Aanpassen} other {Toevoegen}} Bekende Host", - "subtitle": "Door een nieuwe host toe te voegen kun je verbinden met andere servers. Vul hieronder de gegevens in." - }, - "RegistrationDialog": { - "title": "Maak Een Account Aan" - }, - "RequestPasswordResetDialog": { - "title": "Opniew Wachtwoord Aanvragen" - }, - "ResetPasswordDialog": { - "title": "Vraag Wachtwoord Opnieuw Aan" - }, - "AccountActivationForm": { - "error": { - "failed": "Activering account mislukt" - }, - "label": { - "activate": "Activeer Account" - } - }, - "KnownHostForm": { - "help": "Hulp nodig met het toevoegen van een nieuwe host?", - "label": { - "add": "Voeg Host Toe", - "find": "Vind Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Verbind Automatisch", - "forgot": "Wachtwoord Vergeten", - "login": "Log in", - "savePassword": "Wachtwoord Opslaan", - "savedPassword": "Opgeslagen Wachtwoord" - } - }, - "RegisterForm": { - "label": { - "register": "Registreer" - }, - "toast": { - "registerSuccess": "Registratie Geslaagd!" - } - }, - "RequestPasswordResetForm": { - "error": "Opniew wachtwoord aanvragen mislukt", - "mfaEnabled": "Server heeft multi-factor authenticatie ingeschakeld", - "request": "Vraag Reset Token Aan", - "skipRequest": "Ik heb al een reset token" - }, - "ResetPasswordForm": { - "error": "Opnieuw wachtwoord aanvragen mislukt", - "label": { - "reset": "Vraag Wachtwoord Opnieuw Aan" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/pl/translation.json b/webclient/public/locales/pl/translation.json deleted file mode 100644 index abe400792..000000000 --- a/webclient/public/locales/pl/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Polski (Polish)", - "disconnect": "Rozłącz", - "label": { - "confirmPassword": "Potwierdź Hasło", - "confirmSure": "Czy na pewno?", - "country": "Kraj", - "delete": "Usuń", - "email": "Email", - "hostName": "Nazwa Hosta", - "hostAddress": "Adres Hosta", - "password": "Hasło", - "passwordAgain": "Hasło Ponownie:", - "port": "Port", - "realName": "Prawdziwe Imię", - "saveChanges": "Zapisz zmiany", - "token": "Token", - "username": "Nazwa użytkownika" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Hasła nie są zgodne.", - "required": "Wymagane" - }, - "countries": { - "AD": "Andora", - "AE": "Zjednoczone Emiraty Arabskie", - "AF": "Afganistan", - "AG": "Antigua i Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarktyka", - "AR": "Argentyna", - "AS": "Samoa Amerykańskie", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Wyspy Alandzkie", - "AZ": "Azerbejdżan", - "BA": "Bośnia i Hercegowina", - "BB": "Barbados", - "BD": "Bangladesz", - "BE": "Belgia", - "BF": "Burkina Faso", - "BG": "Bułgaria", - "BH": "Bahrajn", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint-Barthélemy", - "BM": "Bermudy", - "BN": "Brunei", - "BO": "Boliwia", - "BQ": "Bonaire, Sint Eustatius i Saba", - "BR": "Brazylia", - "BS": "Bahamy", - "BT": "Bhutan", - "BV": "Wyspa Bouveta", - "BW": "Botwsana", - "BY": "Białoruś", - "BZ": "Belize", - "CA": "Kanada", - "CC": "Wyspy Kokosowe", - "CD": "Demokratyczna Republika Kongo", - "CF": "Republika Środkowoafrykańska", - "CG": "Kongo", - "CH": "Szwajcaria", - "CI": "Wybrzeże Kości Słoniowej", - "CK": "Wyspa Cooka", - "CL": "Chile", - "CM": "Kamerun", - "CN": "Chiny", - "CO": "Kolumbia", - "CR": "Kostaryka", - "CU": "Kuba", - "CV": "Republika Zielonego Przylądka", - "CW": "Curaçao", - "CX": "Wyspa Bożego Narodzenia", - "CY": "Cypr", - "CZ": "Czechy", - "DE": "Niemcy", - "DJ": "Dżibuti", - "DK": "Dania", - "DM": "Dominika", - "DO": "Dominikana", - "DZ": "Algieria", - "EC": "Ekwador", - "EE": "Estonia", - "EG": "Egipt", - "EH": "Sahara Zachodnia", - "ER": "Erytrea", - "ES": "Hiszpania", - "ET": "Etiopia", - "FI": "Finlandia", - "FJ": "Fidżi", - "FK": "Falklandy", - "FM": "Mikronezja", - "FO": "Wyspy Owcze", - "FR": "Francja", - "GA": "Gabon", - "GB": "Wielka Brytania", - "GD": "Grenada", - "GE": "Gruzja", - "GF": "Gujana Francuska", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Grenlandia", - "GM": "Gambia", - "GN": "Gwinea", - "GP": "Gwadelupa", - "GQ": "Gwinea Równikowa", - "GR": "Grecja", - "GS": "Georgia Południowa i Sandwich Południowy", - "GT": "Gwatemala", - "GU": "Guam", - "GW": "Gwinea Bissau", - "GY": "Gujana", - "HK": "Hongkong", - "HM": "Wyspy Heard i McDonalda", - "HN": "Honduras", - "HR": "Chorwacja", - "HT": "Haiti", - "HU": "Węgry", - "ID": "Indonezja", - "IE": "Irlandia", - "IL": "Israel", - "IM": "Wyspa Man", - "IN": "Indie", - "IO": "Brytyjskie Terytorium Oceanu Indyjskiego", - "IQ": "Irak", - "IR": "Iran", - "IS": "Islandia", - "IT": "Włochy", - "JE": "Jersey", - "JM": "Jamajka", - "JO": "Jordania", - "JP": "Japonia", - "KE": "Kenia", - "KG": "Kirgistan", - "KH": "Kambodża", - "KI": "Kiribati", - "KM": "Komory", - "KN": "Saint Kitts i Nevis", - "KP": "Korea Północna", - "KR": "Korea Południowa", - "KW": "Kuwejt", - "KY": "Kajmany", - "KZ": "Kazachstan", - "LA": "Laos", - "LB": "Liban", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Litwa", - "LU": "Luksemburg", - "LV": "Łotwa", - "LY": "Libia", - "MA": "Maroko", - "MC": "Monako", - "MD": "Mołdawia", - "ME": "Czarnogóra", - "MF": "Saint-Martin", - "MG": "Madagaskar", - "MH": "Wyspy Marshalla", - "MK": "Macedonia Północna", - "ML": "Mali", - "MM": "Mjanma", - "MN": "Mongolia", - "MO": "Makau", - "MP": "Mariany Północne", - "MQ": "Martynika", - "MR": "Mauretania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Malediwy", - "MW": "Malawi", - "MX": "Meksyk", - "MY": "Malezja", - "MZ": "Mozambik", - "NA": "Namibia", - "NC": "Nowa Kaledonia", - "NE": "Niger", - "NF": "Wyspa Norfolk", - "NG": "Nigeria", - "NI": "Nikaragua", - "NL": "Holandia", - "NO": "Norwegia", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "Nowa Zelandia", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "Polinezja Francuska", - "PG": "Papua-Nowa Gwinea", - "PH": "Filipiny", - "PK": "Pakistan", - "PL": "Polska", - "PM": "Saint-Pierre i Miquelon", - "PN": "Pitcairn", - "PR": "Portoryko", - "PS": "Palestyna", - "PT": "Portugalia", - "PW": "Palau", - "PY": "Paragwaj", - "QA": "Katar", - "RE": "Reunion", - "RO": "Rumunia", - "RS": "Serbia", - "RU": "Rosja", - "RW": "Rwanda", - "SA": "Arabia Saudyjska", - "SB": "Wyspy Solomona", - "SC": "Seszele", - "SD": "Sudan", - "SE": "Szwecja", - "SG": "Singapur", - "SH": "Wyspa Świętej Heleny, Wyspa Wniebowstąpienia i Tristan da Cunha", - "SI": "Słowenia", - "SJ": "Svalbard i Jan Mayen", - "SK": "Słowacja", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Surinam", - "SS": "Sudan Południowy", - "ST": "Wyspy Świętego Tomasza i Książęca", - "SV": "Salwador", - "SX": "Sint Maarten", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks i Caicos", - "TD": "Czad", - "TF": "FTPA", - "TG": "Togo", - "TH": "Tajlandia", - "TJ": "Tadżykistan", - "TK": "Tokelau", - "TL": "Timor Wschodni", - "TM": "Turkmenistan", - "TN": "Tunezja", - "TO": "Tonga", - "TR": "Turcja", - "TT": "Trynidad i Tobago", - "TV": "Tuvalu", - "TW": "Tajwan", - "TZ": "Tanzania", - "UA": "Ukraina", - "UG": "Uganda", - "UM": "Dalekie Wyspy Mniejsze Stanów Zjednoczonych", - "US": "Stany Zjednoczone", - "UY": "Urugwaj", - "UZ": "Uzbekistan", - "VA": "Watykan", - "VC": "Saint Vincent i Grenadyny", - "VE": "Wenezuela", - "VG": "Brytyjskie Wyspy Dziewicze", - "VI": "Wyspy Dziewicze Stanów Zjednoczonych", - "VN": "Wietnam", - "VU": "Vanuatu", - "WF": "Wallis i Futuna", - "WS": "Samoa", - "YE": "Jemen", - "YT": "Majotta", - "XK": "Kosowo", - "ZA": "Południowa Afryka", - "ZM": "Zambia", - "ZW": "Zimbambwe", - "EU": "Unia Europejska" - }, - "languages": { - "en-US": "Angielski - USA", - "fr": "Francuski", - "nl": "Holenderski", - "pt_BR": "Portugalski - Brazylia" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Dodaj nowego hosta", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "WIEDZIAŁEŚ", - "subtitle": "<1>Cockatrice jest prowadzony przez ochotników<1>którzy uwielbiają karcianki!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "Wieloplatformowy wirtualny stół do wieloosobowych gier karcianych." - }, - "footer": { - "registerPrompt": "Jeszcze nie zarejestrowany?", - "registerAction": "Stwórz konto", - "credit": "Cockatrice jest projektem open source", - "version": "Wersja" - }, - "content": { - "subtitle1": "Graj wieloosobowe karcianki online.", - "subtitle2": "Wieloplatformowy wirtualny stół do wieloosobowych gier karcianych. Zawsze darmowy i wolny." - }, - "toasts": { - "passwordResetSuccessToast": "Hasło Pomyślnie Zresetowany", - "accountActivationSuccess": "Konto Pomyślnie Aktywowane" - } - }, - "UnsupportedContainer": { - "title": "Nieobsługiwana Przeglądarka", - "subtitle1": "Proszę zaktualizuj swoją przeglądarkę i/lub sprawdź jej uprawnienia.", - "subtitle2": "Przeglądanie prywatne może wyłączyć niektóre uprawnienia lub funkcje." - }, - "AccountActivationDialog": { - "title": "Aktywacja Konta", - "subtitle1": "Twoje konto nie zostało jeszcze aktywowane.", - "subtitle2": "Musisz podać token aktywujący, otrzymany w wiadomości email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Dodanie nowego hosta pozwoli Ci połączyć się z innymi serwerami. Podaj jego dane poniżej aby dodać go do listy." - }, - "RegistrationDialog": { - "title": "Stwórz Nowe Konto" - }, - "RequestPasswordResetDialog": { - "title": "Żądanie Resetu Hasła" - }, - "ResetPasswordDialog": { - "title": "Zresetuj hasło" - }, - "AccountActivationForm": { - "error": { - "failed": "Aktywacja konta zakończona niepowodzeniem" - }, - "label": { - "activate": "Aktywuj Konto" - } - }, - "KnownHostForm": { - "help": "Potrzebujesz pomocy z dodaniem nowego hosta?", - "label": { - "add": "Dodaj Hosta", - "find": "Znajdź Hosta" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Połącz automatycznie", - "forgot": "Zapomniałem hasła", - "login": "Login", - "savePassword": "Zapisz hasło", - "savedPassword": "Zapisane Hasło" - } - }, - "RegisterForm": { - "label": { - "register": "Zarejestruj się" - }, - "toast": { - "registerSuccess": "Rejestracja powiodła się!" - } - }, - "RequestPasswordResetForm": { - "error": "Żądanie resetu hasła nie powiodło się.", - "mfaEnabled": "Serwer ma włączone uwierzytelnianie wielkoskładnikowe", - "request": "Zażądaj Token Resetu", - "skipRequest": "Mam już token resetu" - }, - "ResetPasswordForm": { - "error": "Żądanie resetu hasła nie powiodło się.", - "label": { - "reset": "Zresetuj hasło" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/pt_BR/translation.json b/webclient/public/locales/pt_BR/translation.json deleted file mode 100644 index 10ad08603..000000000 --- a/webclient/public/locales/pt_BR/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Português do Brasil (Brazilian Portuguese)", - "disconnect": "Desconectar", - "label": { - "confirmPassword": "Confirmar senha", - "confirmSure": "Você tem certeza?", - "country": "País", - "delete": "Excluir", - "email": "Email", - "hostName": "Nome do host", - "hostAddress": "Endereço do host", - "password": "Senha", - "passwordAgain": "Senha novamente", - "port": "Porta", - "realName": "Nome Real", - "saveChanges": "Salvar Mudanças", - "token": "Ficha", - "username": "Nome de usuário" - }, - "validation": { - "minChars": "Mínimo de {count} {count, plural, one {caractere} other {caracteres}} necessários", - "passwordsMustMatch": "As senhas não correspondem", - "required": "Campo obrigatório" - }, - "countries": { - "AD": "Andorra", - "AE": "Emirados Árabes Unidos", - "AF": "Afeganistão", - "AG": "Antígua e Barbuda", - "AI": "Anguila", - "AL": "Albânia", - "AM": "Armênia", - "AO": "Angola", - "AQ": "Antártica", - "AR": "Argentina", - "AS": "Samoa Americana", - "AT": "Áustria", - "AU": "Austrália", - "AW": "Aruba", - "AX": "Ilhas Aland", - "AZ": "Azerbaijão", - "BA": "Bósnia e Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Bélgica", - "BF": "Burquina Faso", - "BG": "Bulgária", - "BH": "Bahrein", - "BI": "Burundi", - "BJ": "Benim", - "BL": "São Bartolomeu", - "BM": "Bermudas", - "BN": "Brunei", - "BO": "Bolivia", - "BQ": "Bonaire, Santo Eustáquio e Saba", - "BR": "Brasil", - "BS": "Bahamas", - "BT": "Butão", - "BV": "Ilha Bouvet", - "BW": "Botsuana", - "BY": "Bielorrússia", - "BZ": "Belize", - "CA": "Canadá", - "CC": "Ilhas Cocos (Keeling)", - "CD": "RD Congo", - "CF": "República Centro-Africana", - "CG": "República do Congo", - "CH": "Suiça", - "CI": "Costa do Marfim", - "CK": "Ilhas Cook", - "CL": "Chile", - "CM": "Camarões", - "CN": "China", - "CO": "Colômbia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cabo Verde", - "CW": "Curaçao", - "CX": "Ilha Christmas", - "CY": "Chipre", - "CZ": "Tchéquia", - "DE": "Alemanha", - "DJ": "Djibuti", - "DK": "Dinamarca", - "DM": "Dominica", - "DO": "República Dominicana", - "DZ": "Argélia", - "EC": "Equador", - "EE": "Estônia", - "EG": "Egito", - "EH": "Saara Ocidental", - "ER": "Eritreia", - "ES": "Espanha", - "ET": "Etiópia", - "FI": "Finlândia", - "FJ": "Fiji", - "FK": "Ilhas Malvinas", - "FM": "Micronésia", - "FO": "Ilhas Faroé", - "FR": "França", - "GA": "Gabão", - "GB": "Reino Unido", - "GD": "Granada", - "GE": "Geórgia", - "GF": "Guiana Francesa", - "GG": "Guernsey", - "GH": "Gana", - "GI": "Gibraltar", - "GL": "Groenlândia", - "GM": "Gâmbia", - "GN": "Guiné", - "GP": "Guadalupe", - "GQ": "Guiné Equatorial", - "GR": "Grécia", - "GS": "Ilhas Geórgia do Sul e Sandwich do Sul", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guiné-Bissau", - "GY": "Guiana", - "HK": "Hong Kong", - "HM": "Ilha Head e Ilhas McDonald", - "HN": "Honduras", - "HR": "Croácia", - "HT": "Haiti", - "HU": "Hungria", - "ID": "Indonésia", - "IE": "Irlanda", - "IL": "Israel", - "IM": "Ilha de Man", - "IN": "Índia", - "IO": "Território Britânico do Oceano Índico", - "IQ": "Iraque", - "IR": "Irã", - "IS": "Islândia", - "IT": "Itália", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordânia", - "JP": "Japão", - "KE": "Quênia", - "KG": "Quirguistão", - "KH": "Cambodja", - "KI": "Quiribati", - "KM": "Comores", - "KN": "São Cristóvão e Névis", - "KP": "Coréia do Norte", - "KR": "Coreia do Sul", - "KW": "Kuwait", - "KY": "Ilhas Cayman", - "KZ": "Cazaquistão", - "LA": "Laos", - "LB": "Líbano", - "LC": "Santa Lúcia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Libéria", - "LS": "Lesoto", - "LT": "Lituânia", - "LU": "Luxemburgo", - "LV": "Letônia", - "LY": "Líbia", - "MA": "Marrocos", - "MC": "Mônaco", - "MD": "Moldávia", - "ME": "Montenegro", - "MF": "São Martinho (França)", - "MG": "Madagáscar", - "MH": "Ilhas Marshall", - "MK": "Macedônia do Norte", - "ML": "Mali", - "MM": "Mianmar", - "MN": "Mongólia", - "MO": "Macau", - "MP": "Ilhas Marianas do Norte", - "MQ": "Martinica", - "MR": "Mauritânia", - "MS": "Monserrate", - "MT": "Malta", - "MU": "Ilhas Maurício", - "MV": "Maldivas", - "MW": "Malawi", - "MX": "México", - "MY": "Malásia", - "MZ": "Moçambique", - "NA": "Namíbia", - "NC": "Nova Caledônia", - "NE": "Níger", - "NF": "Ilha Norfolk", - "NG": "Nigéria", - "NI": "Nicarágua", - "NL": "Holanda", - "NO": "Noruega", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "Nova Zelândia", - "OM": "Omã", - "PA": "Panamá", - "PE": "Peru", - "PF": "Polinésia Francesa", - "PG": "Papua Nova Guiné", - "PH": "Filipinas", - "PK": "Paquistão", - "PL": "Polônia", - "PM": "Saint-Pierre e Miquelon", - "PN": "Ilhas Pitcairn", - "PR": "Porto Rico", - "PS": "Palestina", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguai", - "QA": "Catar", - "RE": "Ilha da Reunião", - "RO": "Romênia", - "RS": "Sérvia", - "RU": "Rússia", - "RW": "Ruanda", - "SA": "Arábia Saudita", - "SB": "Ilhas Salomão", - "SC": "Seychelles", - "SD": "Sudão", - "SE": "Suécia", - "SG": "Cingapura", - "SH": "Santa Helena, Ascensão e Tristão da Cunha", - "SI": "Eslovênia", - "SJ": "Svalbard e Jan Mayen", - "SK": "Eslováquia", - "SL": "Serra Leoa", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somália", - "SR": "Suriname", - "SS": "Sudão do Sul", - "ST": "São Tomé e Príncipe", - "SV": "El Salvador", - "SX": "São Martinho (Holanda)", - "SY": "Síria", - "SZ": "Essuatíni", - "TC": "Ilhas Turcas e Caicos", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Tailândia", - "TJ": "Tajiquistão", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turcomenistão", - "TN": "Tunísia", - "TO": "Tonga", - "TR": "Turquia", - "TT": "Trindade e Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzânia", - "UA": "Ucrânia", - "UG": "Uganda", - "UM": "Ilhas Menores Distantes dos Estados Unidos", - "US": "Estados Unidos", - "UY": "Uruguai", - "UZ": "Uzbequistão", - "VA": "Santa Sé", - "VC": "São Vicente e Granadinas", - "VE": "Venezuela", - "VG": "Ilhas Virgens Britânicas", - "VI": "Ilhas Virgens Americanas", - "VN": "Vietnã", - "VU": "Vanuatu", - "WF": "Wallis e Futuna", - "WS": "Samoa", - "YE": "Iêmen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "África do Sul", - "ZM": "Zâmbia", - "ZW": "Zimbábue", - "EU": "União Européia" - }, - "languages": { - "en-US": "Inglês - EUA", - "fr": "Francês", - "nl": "Holandês", - "pt_BR": "Português - Brasil" - } - }, - "KnownHosts": { - "label": "Servidor", - "add": "Adicionar novo servidor", - "toast": "Servidor {mode, select, created {criado} deleted {deletado} other {editado}} com sucesso." - }, - "InitializeContainer": { - "title": "VOCÊ SABIA?", - "subtitle": "<1>O Cockatrice é administrado por voluntários<1>que adoram jogos de cartas!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "Uma mesa virtual multiplataforma para jogos multijogador de carta." - }, - "footer": { - "registerPrompt": "Não registrou ainda?", - "registerAction": "Crie uma conta", - "credit": "Cockatrice é um projeto de código aberto", - "version": "Versão" - }, - "content": { - "subtitle1": "Jogue jogos multijogador de cartas online.", - "subtitle2": "Mesa virtual multiplataforma para jogos multijogador de carta. Gratuito para sempre." - }, - "toasts": { - "passwordResetSuccessToast": "Senha redefinida com sucesso", - "accountActivationSuccess": "Conta ativada com sucesso" - } - }, - "UnsupportedContainer": { - "title": "Navegador não suportado", - "subtitle1": "Por favor atualize seu navegador de internet e/ou check as permissões", - "subtitle2": "Observação: a navegação privada faz com que alguns navegadores desativem determinadas permissões ou recursos." - }, - "AccountActivationDialog": { - "title": "Ativação da conta", - "subtitle1": "Sua conta ainda não foi ativada.", - "subtitle2": "Você precisa fornecer o código de ativação recebido por email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Editar} other {Adicionar}} servidor conhecido", - "subtitle": "Adicionar um novo host permite que você se conecte a diferentes servidores. Insira os detalhes abaixo em sua lista de hosts." - }, - "RegistrationDialog": { - "title": "Criar Nova Conta" - }, - "RequestPasswordResetDialog": { - "title": "Solicitar redefinição de senha" - }, - "ResetPasswordDialog": { - "title": "Redefinir Senha" - }, - "AccountActivationForm": { - "error": { - "failed": "Falha na activação de conta" - }, - "label": { - "activate": "Ativar Conta" - } - }, - "KnownHostForm": { - "help": "Necessita de ajuda para adicionar um novo host?", - "label": { - "add": "Adicionar Host", - "find": "Buscar Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Conectar Automaticamente", - "forgot": "Esqueci minha senha", - "login": "Login", - "savePassword": "Salvar Senha", - "savedPassword": "Salvar Senha" - } - }, - "RegisterForm": { - "label": { - "register": "Registrar" - }, - "toast": { - "registerSuccess": "Registro bem-sucedido" - } - }, - "RequestPasswordResetForm": { - "error": "Falha na solicitação de redefinição de senha", - "mfaEnabled": "O servidor tem autenticação de multi-fator ativada", - "request": "Solicitar código de redefinição", - "skipRequest": "Eu já tenho um código de redefinição" - }, - "ResetPasswordForm": { - "error": "Falha na redefinição de senha", - "label": { - "reset": "Redefinir Senha" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/ru/translation.json b/webclient/public/locales/ru/translation.json deleted file mode 100644 index 5e92f790e..000000000 --- a/webclient/public/locales/ru/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Русский (Russian)", - "disconnect": "Прервать подключение", - "label": { - "confirmPassword": "Подтверждение пароля:", - "confirmSure": "Вы уверены?", - "country": "Страна:", - "delete": "Удалить", - "email": "Адрес email:", - "hostName": "Наименование сервера", - "hostAddress": "Адрес сервера", - "password": "Пароль:", - "passwordAgain": "Подтверждение пароля:", - "port": "&Порт:", - "realName": "Настоящее имя:", - "saveChanges": "Сохранить изменения", - "token": "Фишка", - "username": "Имя пользователя:" - }, - "validation": { - "minChars": "Как минимум {count} {count, plural, one {character} other {characters}} необходимо", - "passwordsMustMatch": "Введенные пароли не совпадают.", - "required": "Необходимо" - }, - "countries": { - "AD": "Андорра", - "AE": "ОАЭ", - "AF": "Афганистан", - "AG": "Антигуа и Барбуда", - "AI": "Ангилья", - "AL": "Албания", - "AM": "Армения", - "AO": "Ангола", - "AQ": "Антарктика", - "AR": "Аргентина", - "AS": "Американское Самоа", - "AT": "Австрия", - "AU": "Австралия", - "AW": "Аруба", - "AX": "Аландские острова", - "AZ": "Азербайджан", - "BA": "Босния и Герцеговина", - "BB": "Барбадос", - "BD": "Бангладеш", - "BE": "Бельгия", - "BF": "Буркина Фасо", - "BG": "Болгария", - "BH": "Бахрейн", - "BI": "Бурунди", - "BJ": "Бенин", - "BL": "Сен-Бартелеми", - "BM": "Бермуды", - "BN": "Бруней", - "BO": "Боливия", - "BQ": "Бонэйр", - "BR": "Бразилия", - "BS": "Багамы", - "BT": "Бутан", - "BV": "Остров Буве", - "BW": "Ботсвана", - "BY": "Беларусь", - "BZ": "Белиз", - "CA": "Канада", - "CC": "Кокосовые острова", - "CD": "ДР Конго", - "CF": "ЦАР", - "CG": "Республика Конго", - "CH": "Швейцария", - "CI": "Кот-д'Ивуар", - "CK": "Острова Кука", - "CL": "Чили", - "CM": "Камерун", - "CN": "Китай", - "CO": "Колумбия", - "CR": "Коста-Рика", - "CU": "Куба", - "CV": "Кабо-Верде", - "CW": "Кюрасао", - "CX": "Остров Рождества", - "CY": "Кипр", - "CZ": "Чехия", - "DE": "Германия", - "DJ": "Джибути", - "DK": "Дания", - "DM": "Доминика", - "DO": "Доминиканская Республика", - "DZ": "Алжир", - "EC": "Эквадор", - "EE": "Эстония", - "EG": "Египет", - "EH": "Западная Сахара", - "ER": "Эритрея", - "ES": "Испания", - "ET": "Эфиопия", - "FI": "Финляндия", - "FJ": "Фиджи", - "FK": "Фолклендские острова", - "FM": "Микронезия", - "FO": "Фарерские о-ва", - "FR": "Франция", - "GA": "Габон", - "GB": "Великобритания", - "GD": "Гренада", - "GE": "Грузия", - "GF": "Французская Гвиана", - "GG": "Гернси", - "GH": "Гана", - "GI": "Гибралтар", - "GL": "Гренландия", - "GM": "Гамбия", - "GN": "Гвинея", - "GP": "Гваделупа", - "GQ": "Экваториальная Гвинея", - "GR": "Греция", - "GS": "Южная Георгия и Южные Сандвичевы острова", - "GT": "Гватемала", - "GU": "Гуам", - "GW": "Гвинея-Бисау", - "GY": "Гайана", - "HK": "Гонконг", - "HM": "Остров Херд и остров Макдональд", - "HN": "Гондурас", - "HR": "Хорватия", - "HT": "Гаити", - "HU": "Венгрия", - "ID": "Индонезия", - "IE": "Ирландия", - "IL": "Израиль", - "IM": "о-в Мэн", - "IN": "Индия", - "IO": "Британская территория в Индийском океане ", - "IQ": "Ирак", - "IR": "Иран", - "IS": "Исландия", - "IT": "Италия", - "JE": "Джерси", - "JM": "Ямайка", - "JO": "Иордания", - "JP": "Япония", - "KE": "Кения", - "KG": "Киргизия", - "KH": "Камбоджия", - "KI": "Кирибати", - "KM": "Коморы", - "KN": "Сент-Китс и Невис", - "KP": "Северная Корея", - "KR": "Южная Корея", - "KW": "Кувейт", - "KY": "Каймановы острова", - "KZ": "Казахстан", - "LA": "Лаос", - "LB": "Ливан", - "LC": "Сент-Люсия", - "LI": "Лихтенштейн", - "LK": "Шри-Ланка", - "LR": "Либерия", - "LS": "Лесото", - "LT": "Литва", - "LU": "Люксембург", - "LV": "Латвия", - "LY": "Ливия", - "MA": "Морокко", - "MC": "Монако", - "MD": "Молдавия", - "ME": "Черногория", - "MF": "Сен-Мартен (владение Франции)", - "MG": "Мадагарскар", - "MH": "Маршалловы о-ва", - "MK": "Северная Македония", - "ML": "Мали", - "MM": "Мьянма (Бирма)", - "MN": "Монголия", - "MO": "Макао", - "MP": "Северные Марианские о-ва", - "MQ": "Мартиника", - "MR": "Мавритания", - "MS": "Монтсеррат", - "MT": "Мальта", - "MU": "о. Маврикий", - "MV": "Мальдивы", - "MW": "Малави", - "MX": "Мексика", - "MY": "Малазия", - "MZ": "Мозамбик", - "NA": "Намибия", - "NC": "Новая Каледония", - "NE": "Нигер", - "NF": "Остров Норфолк", - "NG": "Нигерия", - "NI": "Никарагуа", - "NL": "Нидерланды", - "NO": "Норвегия", - "NP": "Непал", - "NR": "Науру", - "NU": "Ниуэ", - "NZ": "Новая Зеландия", - "OM": "Оман", - "PA": "Панама", - "PE": "Перу", - "PF": "Французская Полинезия", - "PG": "Папуа-Новая Гвинея", - "PH": "Филиппины", - "PK": "Пакистан", - "PL": "Польша", - "PM": "Сен-Пьер и Микелон", - "PN": "о-ва Питкэрн", - "PR": "Пуэрто-Рико", - "PS": "Палестина", - "PT": "Португалия", - "PW": "Палау", - "PY": "Парагва", - "QA": "Катар", - "RE": "Реюньон", - "RO": "Румыния", - "RS": "Сербия", - "RU": "Российская Федерация", - "RW": "Руанда", - "SA": "Саудовская Аравия", - "SB": "Соломоновы о-ва", - "SC": "Сейшелы", - "SD": "Судан", - "SE": "Швеция", - "SG": "Сингапур", - "SH": "о. Св. Елены", - "SI": "Словения", - "SJ": "Шпицберген и Ян-Майен", - "SK": "Словакия", - "SL": "Сьерра-Леоне", - "SM": "Сан-Марино", - "SN": "Сенегал", - "SO": "Сомали", - "SR": "Суринам", - "SS": "Южный Судан", - "ST": "Сан-Томе и Принсипи", - "SV": "Сальвадор", - "SX": "Синт-Мартен (Дания)", - "SY": "Сирия", - "SZ": "Эсватини", - "TC": "Острова Теркс и Кайкос", - "TD": "Чад", - "TF": "Французские Южные и Антарктические территории", - "TG": "Того", - "TH": "Тайланд", - "TJ": "Таджикистан", - "TK": "Токелау", - "TL": "Восточный Тимор", - "TM": "Туркмения", - "TN": "Тунис", - "TO": "Тонго", - "TR": "Турция", - "TT": "Тринидад и Тобаго", - "TV": "Тувалу", - "TW": "Тайвань", - "TZ": "Танзания", - "UA": "Украина", - "UG": "Уганда", - "UM": "Внешние малые о-ва (США)", - "US": "США", - "UY": "Уругвай", - "UZ": "Узбекистан", - "VA": "Святой Престол", - "VC": "Сент-Винсент и Гренадины", - "VE": "Венесуэла", - "VG": "Британские Виргинские острова", - "VI": "Американские Виргинские острова", - "VN": "Вьетнам", - "VU": "Вануату", - "WF": "о-ва Уоллис и Футуна", - "WS": "Самоа", - "YE": "Йемен", - "YT": "Майотта", - "XK": "Косово", - "ZA": "ЮАР", - "ZM": "Замбия", - "ZW": "Зимбабве", - "EU": "Европейский Союз" - }, - "languages": { - "en-US": "Английский - США", - "fr": "Французский", - "nl": "Датский", - "pt_BR": "Португальский - Бразилия" - } - }, - "KnownHosts": { - "label": "&Хост", - "add": "Добавить новый сервер", - "toast": "Сервер успешно {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "ЗНАЕТЕ ЛИ ВЫ", - "subtitle": "<1>Cockatrice поддерживается энтузиастами<1>которые любят карточные игры!" - }, - "LoginContainer": { - "header": { - "title": "Логин", - "subtitle": "Кросс-платформенный виртуальный стол для мультиплеерных ККИ" - }, - "footer": { - "registerPrompt": "Ещё не зарегистрированы?", - "registerAction": "Создать учетную запись", - "credit": "Cockatrice - проект с открытым исходным кодом", - "version": "Версия" - }, - "content": { - "subtitle1": "Играйте в мультиплеерные ККИ онлайн", - "subtitle2": "Кросс-платформенный виртуальный стол для мультиплеерных ККИ. Всегда будет бесплатным." - }, - "toasts": { - "passwordResetSuccessToast": "Пароль сброшен успешно", - "accountActivationSuccess": "Учетная запись активирована успешно" - } - }, - "UnsupportedContainer": { - "title": "Браузер не поддерживается", - "subtitle1": "Обновите бразуер и/или проверьте права доступа", - "subtitle2": "Обратите внимание, что использование анонимных браузеров может привести к неработоспособности некоторых фич и проблемами с правами доступа" - }, - "AccountActivationDialog": { - "title": "Активация учетной записи", - "subtitle1": "Ваша учетная запись не активирована", - "subtitle2": "Ваш аккаунт пока не активирован. Вам необходимо следовать указаниям по активации, отправленным на ваш email" - }, - "KnownHostDialog": { - "title": "{модифицировать, выбрать, редактировать {Edit} другой {Add}} известный хост", - "subtitle": "Добавление нового хоста позволит вам присоединяться к различным серверам. Введите данные о сервере в ваш список хостов" - }, - "RegistrationDialog": { - "title": "Создать новый аккаунт" - }, - "RequestPasswordResetDialog": { - "title": "Запросить сброс пароля" - }, - "ResetPasswordDialog": { - "title": "Сбросить пароль" - }, - "AccountActivationForm": { - "error": { - "failed": "Не удалось активировать аккаунт" - }, - "label": { - "activate": "Активировать аккаунт" - } - }, - "KnownHostForm": { - "help": "Нужна помощь в добавлении нового хоста?", - "label": { - "add": "Добавить хост", - "find": "Найти хост" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Автоматическое подключение", - "forgot": "Забыли пароль", - "login": "Логин", - "savePassword": "Сохранить пароль", - "savedPassword": "Сохранённый пароль" - } - }, - "RegisterForm": { - "label": { - "register": "Зарегистрироваться" - }, - "toast": { - "registerSuccess": "Регистрация прошла успешно!" - } - }, - "RequestPasswordResetForm": { - "error": "Не удалось запросить сброс пароля", - "mfaEnabled": "На сервере включена многофакторная аутентификация", - "request": "Запросить токен сброса", - "skipRequest": "У меня уже есть токен сброса" - }, - "ResetPasswordForm": { - "error": "Не удалось сбросить пароль", - "label": { - "reset": "Сбросить пароля" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/tok/translation.json b/webclient/public/locales/tok/translation.json deleted file mode 100644 index 46437f558..000000000 --- a/webclient/public/locales/tok/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "Toki Pona", - "disconnect": "mi o weka tan kulupu", - "label": { - "confirmPassword": "nimi ken li pona ala pona", - "confirmSure": "ni li pona ala pona", - "country": "ma", - "delete": "mi o weka e ni", - "email": "nimi Email", - "hostName": "nimi pi ilo lawa", - "hostAddress": "ma pi ilo lawa", - "password": "nimi ken pi kama sina", - "passwordAgain": "o pana sin e nimi ken pi kama sina", - "port": "nanpa pi ilo lawa", - "realName": "nimi sina lon", - "saveChanges": "mi o awen e ante sina", - "token": "ijo lili", - "username": "nimi sina" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "nimi ken nanpa wan li sama ala nimi ken nanpa tu", - "required": "o ni" - }, - "countries": { - "AD": "ma Antola", - "AE": "United Arab Emirates", - "AF": "ma Akanisan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "ma Sipe", - "AM": "ma Aja", - "AO": "ma Ankola", - "AQ": "ma Antasika", - "AR": "ma Alensina", - "AS": "American Samoa", - "AT": "ma Esalasi", - "AU": "ma Oselija", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "ma Papeto", - "BD": "ma Panla", - "BE": "ma Pesije", - "BF": "ma Pukinapaso", - "BG": "ma Pokasi", - "BH": "ma Palani", - "BI": "Burundi", - "BJ": "ma Penen", - "BL": "Saint Barthélemy", - "BM": "ma Pemuta", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "ma Pasila", - "BS": "ma Pawama", - "BT": "ma Putan", - "BV": "Bouvet Island", - "BW": "ma Posuwana", - "BY": "ma Pelalusi", - "BZ": "Belize", - "CA": "ma Kanata", - "CC": "Cocos (Keeling) Islands", - "CD": "ma DR Konko", - "CF": "ma Santapiken", - "CG": "ma Konko", - "CH": "ma Suwasi", - "CI": "ma Kowisa", - "CK": "Cook Islands", - "CL": "ma Sile", - "CM": "ma Kamelun", - "CN": "ma Sonko", - "CO": "Colombia", - "CR": "ma Kosalika", - "CU": "ma Kupa", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "ma Kiposi", - "CZ": "ma Seki", - "DE": "ma Tosi", - "DJ": "ma Sipusi", - "DK": "ma Tansi", - "DM": "ma Watukupuli", - "DO": "ma Tominika", - "DZ": "ma Sasali", - "EC": "ma Ekato", - "EE": "ma Esi", - "EG": "ma Masu", - "EH": "Western Sahara", - "ER": "ma Eliteja", - "ES": "ma Epanja", - "ET": "ma Isijopija", - "FI": "ma Sumi", - "FJ": "ma Pisi", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "ma Kanse", - "GA": "ma Kapon", - "GB": "ma Juke", - "GD": "ma Kenata", - "GE": "ma Katelo", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "ma Kana", - "GI": "Gibraltar", - "GL": "ma Kalalinuna", - "GM": "ma Kanpija", - "GN": "ma Kine", - "GP": "Guadeloupe", - "GQ": "ma Kinejekatolija", - "GR": "ma Elena", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "ma Katemala", - "GU": "Guam", - "GW": "ma Kinepisa", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "ma Ontula", - "HR": "ma Lowasi", - "HT": "ma Awisi", - "HU": "ma Mosijo", - "ID": "ma Intonesija", - "IE": "ma Alan", - "IL": "ma Isale", - "IM": "Isle of Man", - "IN": "ma Palata", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "ma Isilan", - "IT": "ma Italija", - "JE": "Jersey", - "JM": "ma Sameka", - "JO": "ma Utun", - "JP": "ma Nijon", - "KE": "ma Kenja", - "KG": "Kyrgyzstan", - "KH": "ma Kanpusi", - "KI": "ma Kilipasi", - "KM": "ma Komo", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "ma Anku", - "KW": "ma Kuwasi", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "ma Lunpan", - "LC": "Saint Lucia", - "LI": "ma Lisensan", - "LK": "ma Lanka", - "LR": "ma Lapewija", - "LS": "ma Lesoto", - "LT": "ma Lijatuwa", - "LU": "ma Lusepu", - "LV": "ma Lawi", - "LY": "ma Lipija", - "MA": "ma Malipe", - "MC": "Monaco", - "MD": "ma Motowa", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "ma Maketonija", - "ML": "ma Mali", - "MM": "ma Mijama", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "ma Mulitanija", - "MS": "Montserrat", - "MT": "Malta", - "MU": "ma Mowisi", - "MV": "Maldives", - "MW": "Malawi", - "MX": "ma Mesiko", - "MY": "ma Malasija", - "MZ": "ma Mosanpi", - "NA": "ma Namipija", - "NC": "New Caledonia", - "NE": "ma Nise", - "NF": "Norfolk Island", - "NG": "ma Naselija", - "NI": "Nicaragua", - "NL": "ma Netelan", - "NO": "ma Nosiki", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "ma Nusilan", - "OM": "ma Uman", - "PA": "ma Panama", - "PE": "ma Pelu", - "PF": "French Polynesia", - "PG": "ma Papuwanijukini", - "PH": "ma Pilipina", - "PK": "ma Pakisan", - "PL": " ma Posuka", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "ma Pilisin", - "PT": "ma Potuke", - "PW": "Palau", - "PY": "ma Palakawi", - "QA": "Qatar", - "RE": "Réunion", - "RO": "ma Lomani", - "RS": "ma Sopisi", - "RU": "ma Losi", - "RW": "ma Luwanta", - "SA": "ma Sawusi", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "ma Sutan", - "SE": "ma Wensa", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "ma Lowensina", - "SJ": "Svalbard and Jan Mayen", - "SK": "ma Lowenki", - "SL": "ma Sijelalijon", - "SM": "ma Samalino", - "SN": "ma Seneka", - "SO": "ma Somalija", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "ma Sulija", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "ma Sate", - "TF": "TAAF", - "TG": "ma Toko", - "TH": "ma Tawi", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "ma Tunisi", - "TO": " ma Tona", - "TR": "ma Tuki", - "TT": "ma Sinita", - "TV": "ma Tuwalu", - "TW": "ma Tawan", - "TZ": "ma Tansanija", - "UA": "ma Ukawina", - "UG": "ma Ukanta", - "UM": "United States Minor Outlying Islands", - "US": "ma Mewika", - "UY": "ma Ulukawi", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "ma SVG", - "VE": "ma Penesuwela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "ma Wije", - "VU": "ma Wanuwatu", - "WF": "Wallis and Futuna", - "WS": "ma Samowa", - "YE": "ma Jamanija", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "ma Setapika", - "ZM": "ma Sanpija", - "ZW": "ma Sinpapuwe", - "EU": "ma kulupu Elopa" - }, - "languages": { - "en-US": "toki Inli", - "fr": "toki Kanse", - "nl": "toki Netelan", - "pt_BR": "toki Potuke pi ma Pasila" - } - }, - "KnownHosts": { - "label": "ilo lawa", - "add": "mi o ken e ilo lawa sin", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "sina sona ala sona e ni:", - "subtitle": "<1>jan li pali e ilo Cockatrice lon wile taso! <1>musi lipu li pona tawa ona!" - }, - "LoginContainer": { - "header": { - "title": "mi o kama e sina lon kulupu", - "subtitle": "ilo Cockatrice li ken e musi lipu ale. nasin mute ilo li ken lon ona." - }, - "footer": { - "registerPrompt": "sina jo ala jo e sijelo kulupu?", - "registerAction": "mi o pali e sijelo kulupu sina", - "credit": "insa pi ilo Cockatrice li len ala", - "version": "nanpa ante" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - }, - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - }, - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - }, - "RegistrationDialog": { - "title": "Create New Account" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "mi o sin e nimi ken" - }, - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - }, - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "nimi ken pi kama sina li weka tan sona", - "login": "mi o kama e sina lon kulupu", - "savePassword": "mi o awen e nimi ken", - "savedPassword": "mi awen e nimi ken" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "wile ante pi nimi ken li pakala", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "ante pi nimi ken li pakala", - "label": { - "reset": "mi o sin e nimi ken" - } - } -} \ No newline at end of file diff --git a/webclient/public/locales/yue/translation.json b/webclient/public/locales/yue/translation.json deleted file mode 100644 index b14041496..000000000 --- a/webclient/public/locales/yue/translation.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "", - "disconnect": "斷開連線", - "label": { - "confirmPassword": "確認新密碼:", - "confirmSure": "你確定嗎?", - "country": "國家:", - "delete": "删除", - "email": "電郵:", - "hostName": "主機名稱", - "hostAddress": "主機地址", - "password": "密碼:", - "passwordAgain": "再次輸入密碼:", - "port": "端口:", - "realName": "實名:", - "saveChanges": "儲存變更", - "token": "令牌", - "username": "用戶名稱:" - }, - "validation": { - "minChars": "最少需要{數目} {數目,眾數,單{字元}或其他{眾字元}}", - "passwordsMustMatch": "新舊密碼不一致.", - "required": "必須" - }, - "countries": { - "AD": "安道爾", - "AE": "阿拉伯聯合大公國", - "AF": "阿富汗", - "AG": "安地卡及巴布達", - "AI": "安圭拉", - "AL": "阿爾巴尼亞", - "AM": "亞美尼亞", - "AO": "安哥拉", - "AQ": "南極洲", - "AR": "阿根廷", - "AS": "美屬薩摩亞", - "AT": "奧地利", - "AU": "澳洲", - "AW": "阿魯巴", - "AX": "奧蘭群島", - "AZ": "亞塞拜然", - "BA": "波士尼亞與赫塞哥維納", - "BB": "巴貝多", - "BD": "孟加拉", - "BE": "比利時", - "BF": "布吉納法索", - "BG": "保加利亞", - "BH": "巴林", - "BI": "蒲隆地", - "BJ": "貝南", - "BL": "聖巴泰勒米", - "BM": "百慕達", - "BN": "汶萊達魯薩蘭國", - "BO": "玻利維亞", - "BQ": "博內爾島、聖尤斯特歇斯島和薩巴島", - "BR": "巴西", - "BS": "巴哈馬", - "BT": "不丹", - "BV": "布韋島", - "BW": "波札那", - "BY": "白俄羅斯", - "BZ": "貝里斯", - "CA": "加拿大", - "CC": "科科斯(基林)群島", - "CD": "剛果民主共和國", - "CF": "中非共和國", - "CG": "剛果共和國", - "CH": "瑞士", - "CI": "科特迪瓦", - "CK": "庫克群島", - "CL": "智利", - "CM": "喀麥隆", - "CN": "中國", - "CO": "哥倫比亞", - "CR": "哥斯大黎加", - "CU": "古巴", - "CV": "維德角", - "CW": "庫拉索", - "CX": "聖誕島", - "CY": "賽普勒斯", - "CZ": "捷克", - "DE": "德國", - "DJ": "吉布地", - "DK": "丹麥", - "DM": "多明尼加", - "DO": "多明尼加共和國", - "DZ": "阿爾及利亞", - "EC": "厄瓜多", - "EE": "愛沙尼亞", - "EG": "埃及", - "EH": "西撒哈拉", - "ER": "厄利垂亞", - "ES": "西班牙", - "ET": "衣索比亞", - "FI": "芬蘭", - "FJ": "斐濟", - "FK": "福克蘭群島", - "FM": "密克羅尼西亞", - "FO": "法羅群島", - "FR": "法國", - "GA": "加彭", - "GB": "英國", - "GD": "格瑞那達", - "GE": "喬治亞", - "GF": "法屬圭亞那", - "GG": "根西島", - "GH": "迦納", - "GI": "直布羅陀", - "GL": "格陵蘭", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Add new host", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "DID YOU KNOW", - "subtitle": "<1>Cockatrice is run by volunteers<1>that love card games!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "A cross-platform virtual tabletop for multiplayer card games." - }, - "footer": { - "registerPrompt": "Not registered yet?", - "registerAction": "Create an account", - "credit": "Cockatrice is an open source project", - "version": "Version" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - }, - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - }, - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - }, - "RegistrationDialog": { - "title": "Create New Account" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "Reset Password" - }, - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - }, - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Login", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Reset Password" - } - } -} \ No newline at end of file diff --git a/webclient/public/logo192.png b/webclient/public/logo192.png deleted file mode 100644 index fa313abf5..000000000 Binary files a/webclient/public/logo192.png and /dev/null differ diff --git a/webclient/public/logo512.png b/webclient/public/logo512.png deleted file mode 100644 index bd5d4b5e2..000000000 Binary files a/webclient/public/logo512.png and /dev/null differ diff --git a/webclient/public/manifest.json b/webclient/public/manifest.json deleted file mode 100644 index 080d6c77a..000000000 --- a/webclient/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "React App", - "name": "Create React App Sample", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - }, - { - "src": "logo192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "logo512.png", - "type": "image/png", - "sizes": "512x512" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/webclient/public/pb/.gitignore b/webclient/public/pb/.gitignore deleted file mode 100644 index 571442132..000000000 --- a/webclient/public/pb/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore all files -* -# Except gitignore -!.gitignore \ No newline at end of file diff --git a/webclient/public/reset.css b/webclient/public/reset.css deleted file mode 100644 index af944401f..000000000 --- a/webclient/public/reset.css +++ /dev/null @@ -1,48 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} \ No newline at end of file diff --git a/webclient/public/robots.txt b/webclient/public/robots.txt deleted file mode 100644 index 01b0f9a10..000000000 --- a/webclient/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * diff --git a/webclient/src/api/AdminService.tsx b/webclient/src/api/AdminService.tsx deleted file mode 100644 index 623ad546a..000000000 --- a/webclient/src/api/AdminService.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { AdminCommands } from 'websocket'; - -export class AdminService { - static adjustMod(userName: string, shouldBeMod?: boolean, shouldBeJudge?: boolean): void { - AdminCommands.adjustMod(userName, shouldBeMod, shouldBeJudge); - } - - static reloadConfig(): void { - AdminCommands.reloadConfig(); - } - - static shutdownServer(reason: string, minutes: number): void { - AdminCommands.shutdownServer(reason, minutes); - } - - static updateServerMessage(): void { - AdminCommands.updateServerMessage(); - } -} diff --git a/webclient/src/api/AuthenticationService.tsx b/webclient/src/api/AuthenticationService.tsx deleted file mode 100644 index 7b3a46988..000000000 --- a/webclient/src/api/AuthenticationService.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { StatusEnum, User, WebSocketConnectReason, WebSocketConnectOptions } from 'types'; -import { SessionCommands, webClient } from 'websocket'; -import { ProtoController } from 'websocket/services/ProtoController'; - -export class AuthenticationService { - static login(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.LOGIN); - } - - static testConnection(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.TEST_CONNECTION); - } - - static register(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.REGISTER); - } - - static activateAccount(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.ACTIVATE_ACCOUNT); - } - - static resetPasswordRequest(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.PASSWORD_RESET_REQUEST); - } - - static resetPasswordChallenge(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.PASSWORD_RESET_CHALLENGE); - } - - static resetPassword(options: WebSocketConnectOptions): void { - SessionCommands.connect(options, WebSocketConnectReason.PASSWORD_RESET); - } - - static disconnect(): void { - SessionCommands.disconnect(); - } - - static isConnected(state: number): boolean { - return state === StatusEnum.LOGGED_IN; - } - - static isModerator(user: User): boolean { - const moderatorLevel = ProtoController.root.ServerInfo_User.UserLevelFlag.IsModerator; - // @TODO tell cockatrice not to do this so shittily - return (user.userLevel & moderatorLevel) === moderatorLevel; - } - - static isAdmin() { - - } - - static connectionAttemptMade() { - return webClient.connectionAttemptMade; - } -} diff --git a/webclient/src/api/ModeratorService.tsx b/webclient/src/api/ModeratorService.tsx deleted file mode 100644 index 6c22ee55e..000000000 --- a/webclient/src/api/ModeratorService.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { ModeratorCommands } from 'websocket'; -import { LogFilters } from 'types'; - -export class ModeratorService { - static banFromServer(minutes: number, userName?: string, address?: string, reason?: string, - visibleReason?: string, clientid?: string, removeMessages?: number): void { - ModeratorCommands.banFromServer(minutes, userName, address, reason, visibleReason, clientid, removeMessages); - } - - static getBanHistory(userName: string): void { - ModeratorCommands.getBanHistory(userName); - } - - static getWarnHistory(userName: string): void { - ModeratorCommands.getWarnHistory(userName); - } - - static getWarnList(modName: string, userName: string, userClientid: string): void { - ModeratorCommands.getWarnList(modName, userName, userClientid); - } - - static viewLogHistory(filters: LogFilters): void { - ModeratorCommands.viewLogHistory(filters); - } - - static warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void { - ModeratorCommands.warnUser(userName, reason, clientid, removeMessages); - } -} diff --git a/webclient/src/api/RoomsService.tsx b/webclient/src/api/RoomsService.tsx deleted file mode 100644 index bfb63d92a..000000000 --- a/webclient/src/api/RoomsService.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { RoomCommands, SessionCommands } from 'websocket'; - -export class RoomsService { - static joinRoom(roomId: number): void { - SessionCommands.joinRoom(roomId); - } - - static leaveRoom(roomId: number): void { - RoomCommands.leaveRoom(roomId); - } - - static roomSay(roomId: number, message: string): void { - RoomCommands.roomSay(roomId, message); - } -} diff --git a/webclient/src/api/SessionService.tsx b/webclient/src/api/SessionService.tsx deleted file mode 100644 index 2787f098d..000000000 --- a/webclient/src/api/SessionService.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { SessionCommands } from 'websocket'; - -export class SessionService { - static addToBuddyList(userName: string) { - SessionCommands.addToBuddyList(userName); - } - - static removeFromBuddyList(userName: string) { - SessionCommands.removeFromBuddyList(userName); - } - - static addToIgnoreList(userName: string) { - SessionCommands.addToIgnoreList(userName); - } - - static removeFromIgnoreList(userName: string) { - SessionCommands.removeFromIgnoreList(userName); - } - - static changeAccountPassword(oldPassword: string, newPassword: string, hashedNewPassword?: string): void { - SessionCommands.accountPassword(oldPassword, newPassword, hashedNewPassword); - } - - static changeAccountDetails(passwordCheck: string, realName?: string, email?: string, country?: string): void { - SessionCommands.accountEdit(passwordCheck, realName, email, country); - } - - static changeAccountImage(image: Uint8Array): void { - SessionCommands.accountImage(image); - } - - static sendDirectMessage(userName: string, message: string): void { - SessionCommands.message(userName, message); - } - - static getUserInfo(userName: string): void { - SessionCommands.getUserInfo(userName); - } - - static getUserGames(userName: string): void { - SessionCommands.getGamesOfUser(userName); - } -} diff --git a/webclient/src/api/index.ts b/webclient/src/api/index.ts deleted file mode 100644 index c4f67092e..000000000 --- a/webclient/src/api/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { AdminService } from './AdminService'; -export { AuthenticationService } from './AuthenticationService'; -export { ModeratorService } from './ModeratorService'; -export { RoomsService } from './RoomsService'; -export { SessionService } from './SessionService'; diff --git a/webclient/src/common.i18n.json b/webclient/src/common.i18n.json deleted file mode 100644 index 64a102c38..000000000 --- a/webclient/src/common.i18n.json +++ /dev/null @@ -1,286 +0,0 @@ -{ - "Common": { - "language": "English", - "disconnect": "Disconnect", - "label": { - "confirmPassword": "Confirm Password", - "confirmSure": "Are you sure?", - "country": "Country", - "delete": "Delete", - "email": "Email", - "hostName": "Host Name", - "hostAddress": "Host Address", - "password": "Password", - "passwordAgain": "Password Again", - "port": "Port", - "realName": "Real Name", - "saveChanges": "Save Changes", - "token": "Token", - "username": "Username" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Passwords don't match", - "required": "Required" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - } -} diff --git a/webclient/src/components/Card/Card.css b/webclient/src/components/Card/Card.css deleted file mode 100644 index 976618634..000000000 --- a/webclient/src/components/Card/Card.css +++ /dev/null @@ -1,4 +0,0 @@ -.card { - width: 100%; - height: 100%; -} diff --git a/webclient/src/components/Card/Card.tsx b/webclient/src/components/Card/Card.tsx deleted file mode 100644 index f89622c3b..000000000 --- a/webclient/src/components/Card/Card.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// eslint-disable-next-line -import React, { useMemo, useState } from 'react'; - -import { CardDTO } from 'services'; - -import './Card.css'; - -interface CardProps { - card: CardDTO; -} - -const Card = ({ card }: CardProps) => { - const src = `https://api.scryfall.com/cards/${card?.identifiers?.scryfallId}?format=image`; - - return card && ( - {card?.name} - ); -} - -export default Card; diff --git a/webclient/src/components/CardDetails/CardDetails.css b/webclient/src/components/CardDetails/CardDetails.css deleted file mode 100644 index 7009049d2..000000000 --- a/webclient/src/components/CardDetails/CardDetails.css +++ /dev/null @@ -1,45 +0,0 @@ -.cardDetails { - padding: 10px; - width: calc(400px * .716); - font-size: 10px; -} - -.cardDetails-card { - height: 400px; - margin: 0 auto; -} - -.cardDetails-attribute { - display: flex; - justify-content: space-between; - align-items: baseline; -} - -.cardDetails-attributes { - margin: 10px 0; -} - -.cardDetails-attribute__label { - text-transform: uppercase; - font-size: 10px; - margin-right: 10px; -} - -.cardDetails-attribute__value { - text-align: right; -} - -.cardDetails-text { - font-size: 12px; - padding: 5px; - background: rgba(0, 0, 0, .15); - white-space: pre-line; -} - -.cardDetails-text__flavor { - font-style: italic; -} - -.cardDetails-text__current:not(:empty) + .cardDetails-text__flavor { - margin-top: 10px; -} diff --git a/webclient/src/components/CardDetails/CardDetails.tsx b/webclient/src/components/CardDetails/CardDetails.tsx deleted file mode 100644 index 84196fda6..000000000 --- a/webclient/src/components/CardDetails/CardDetails.tsx +++ /dev/null @@ -1,130 +0,0 @@ -// eslint-disable-next-line -import React, { useMemo, useState } from 'react'; - -import { CardDTO } from 'services'; - -import Card from '../Card/Card'; - -import './CardDetails.css'; - -interface CardProps { - card: CardDTO; -} - -// @TODO: add missing fields (loyalty, hand, etc) - -const CardDetails = ({ card }: CardProps) => { - return ( -
-
- -
- - { - card && ( -
-
-
- Name: - {card.name} -
- - { - (!card.power && !card.toughness) ? null : ( -
- P/T: - {card.power || 0}/{card.toughness || 0} -
- ) - } - - { - !card.manaCost ? null : ( -
- Cost: - {card.manaCost.replace(/\{|\}/g, '')} -
- ) - } - - { - !card.convertedManaCost ? null : ( -
- CMC: - {card.convertedManaCost} -
- ) - } - - { - !card.colorIdentity?.length ? null : ( -
- Identity: - {card.colorIdentity.join('')} -
- ) - } - - { - !card.colors?.length ? null : ( -
- Color(s): - {card.colors.join('')} -
- ) - } - - { - !card.types?.length ? null : ( -
- Main Type: - {card.types.join(', ')} -
- ) - } - - { - !card.type ? null : ( -
- Type: - {card.type} -
- ) - } - - { - !card.side ? null : ( -
- Side: - {card.side} -
- ) - } - - { - !card.layout ? null : ( -
- Layout: - {card.layout} -
- ) - } -
- -
-
- {card.text?.trim()} -
- -
- {card.flavorText?.trim()} -
-
-
- ) - } -
- ); -} - -export default CardDetails; diff --git a/webclient/src/components/CheckboxField/CheckboxField.css b/webclient/src/components/CheckboxField/CheckboxField.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/webclient/src/components/CheckboxField/CheckboxField.tsx b/webclient/src/components/CheckboxField/CheckboxField.tsx deleted file mode 100644 index 562687489..000000000 --- a/webclient/src/components/CheckboxField/CheckboxField.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import Checkbox from '@mui/material/Checkbox'; -import FormControlLabel from '@mui/material/FormControlLabel'; - -const CheckboxField = (props) => { - const { input: { value, onChange }, label, ...args } = props; - - // @TODO this isnt unchecking properly - return ( - onChange(checked)} - color="primary" - /> - } - /> - ); -}; - -export default CheckboxField; diff --git a/webclient/src/components/CountryDropdown/CountryDropdown.css b/webclient/src/components/CountryDropdown/CountryDropdown.css deleted file mode 100644 index 9b1679516..000000000 --- a/webclient/src/components/CountryDropdown/CountryDropdown.css +++ /dev/null @@ -1,13 +0,0 @@ -.CountryDropdown { - width: 100%; -} - -.CountryDropdown-item { - display: flex; - align-items: center; -} - -.CountryDropdown-item__image { - width: 1.5em; - margin-right: 1em; -} diff --git a/webclient/src/components/CountryDropdown/CountryDropdown.tsx b/webclient/src/components/CountryDropdown/CountryDropdown.tsx deleted file mode 100644 index 09b1cf71e..000000000 --- a/webclient/src/components/CountryDropdown/CountryDropdown.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Select, MenuItem } from '@mui/material'; -import FormControl from '@mui/material/FormControl'; -import InputLabel from '@mui/material/InputLabel'; -import { useTranslation } from 'react-i18next'; - -import { useLocaleSort } from 'hooks'; -import { Images } from 'images/Images'; -import { countryCodes } from 'types'; - - -import './CountryDropdown.css'; - -const CountryDropdown = ({ input: { onChange } }) => { - const [value, setValue] = useState(''); - const { t } = useTranslation(); - - useEffect(() => onChange(value), [value]); - - const translateCountry = country => t(`Common.countries.${country}`); - const sortedCountries = useLocaleSort(countryCodes, translateCountry); - - return ( - - Country - - - ) -}; - -export default CountryDropdown; diff --git a/webclient/src/components/Guard/AuthGuard.tsx b/webclient/src/components/Guard/AuthGuard.tsx deleted file mode 100644 index eea42dc7b..000000000 --- a/webclient/src/components/Guard/AuthGuard.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { Navigate } from 'react-router-dom'; - -import { ServerSelectors } from 'store'; -import { RouteEnum } from 'types'; - -import { AuthenticationService } from 'api'; - -const AuthGuard = ({ state }: AuthGuardProps) => { - return !AuthenticationService.isConnected(state) - ? - :
; -}; - -interface AuthGuardProps { - state: number; -} - -const mapStateToProps = state => ({ - state: ServerSelectors.getState(state), -}); - -export default connect(mapStateToProps)(AuthGuard); diff --git a/webclient/src/components/Guard/ModGuard.tsx b/webclient/src/components/Guard/ModGuard.tsx deleted file mode 100644 index c8bc6d663..000000000 --- a/webclient/src/components/Guard/ModGuard.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { Navigate } from 'react-router-dom'; - -import { ServerSelectors } from 'store'; -import { User } from 'types'; - -import { AuthenticationService } from 'api'; -import { RouteEnum } from 'types'; - -class ModGuard extends Component { - render() { - return !AuthenticationService.isModerator(this.props.user) - ? - : ''; - } -}; - -interface ModGuardProps { - user: User; -} - -const mapStateToProps = state => ({ - user: ServerSelectors.getUser(state), -}); - -export default connect(mapStateToProps)(ModGuard); diff --git a/webclient/src/components/InputAction/InputAction.css b/webclient/src/components/InputAction/InputAction.css deleted file mode 100644 index 25e784a3a..000000000 --- a/webclient/src/components/InputAction/InputAction.css +++ /dev/null @@ -1,19 +0,0 @@ -.input-action { - display: flex; - width: 100%; - align-items: center; -} - -.input-action, -.input-action__item, -.input-action__submit { - padding: 5px; -} - -.input-action__item { - width: 100%; - height: 100%; -} -.input-action__item > div { - margin: 0; -} \ No newline at end of file diff --git a/webclient/src/components/InputAction/InputAction.tsx b/webclient/src/components/InputAction/InputAction.tsx deleted file mode 100644 index 459b89904..000000000 --- a/webclient/src/components/InputAction/InputAction.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { Field } from 'react-final-form' -import Button from '@mui/material/Button'; - -import { InputField } from 'components'; - -import './InputAction.css'; - -const InputAction = ({ action, label, name, validate, disabled }) => ( -
-
- -
-
- -
-
-); - -InputAction.defaultProps = { - disabled: false, - validate: () => false, -} - -export default InputAction; diff --git a/webclient/src/components/InputField/InputField.css b/webclient/src/components/InputField/InputField.css deleted file mode 100644 index 819f7112a..000000000 --- a/webclient/src/components/InputField/InputField.css +++ /dev/null @@ -1,20 +0,0 @@ -.InputField { - position: relative; -} - -.InputField-validation { - position: absolute; - top: 0; - right: 0; - transform: translateY(-50%); - font-weight: bold; -} - -.InputField-error { - display: flex; - align-items: center; -} - -.InputField-error svg { - margin-left: 4px; -} diff --git a/webclient/src/components/InputField/InputField.tsx b/webclient/src/components/InputField/InputField.tsx deleted file mode 100644 index 3299ba383..000000000 --- a/webclient/src/components/InputField/InputField.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; -import TextField from '@mui/material/TextField'; -import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined'; - -import './InputField.css'; - -const PREFIX = 'InputField'; - -const classes = { - root: `${PREFIX}-root` -}; - -const Root = styled('div')(({ theme }) => ({ - [`&.${classes.root}`]: { - '& .InputField-error': { - color: theme.palette.error.main - }, - - '& .InputField-warning': { - color: theme.palette.warning.main - }, - }, -})); - -const InputField = ({ input, meta, ...args }) => { - const { touched, error, warning } = meta; - - return ( - - { touched && ( -
- { - (error && -
- {error} - -
- ) || - - (warning &&
{warning}
) - } -
- ) } - - -
- ); -}; - -export default InputField; diff --git a/webclient/src/components/KnownHosts/KnownHosts.css b/webclient/src/components/KnownHosts/KnownHosts.css deleted file mode 100644 index 32a3a9fcc..000000000 --- a/webclient/src/components/KnownHosts/KnownHosts.css +++ /dev/null @@ -1,70 +0,0 @@ -.KnownHosts { -} - -.KnownHosts-form { - width: 100%; - position: relative; -} - -.KnownHosts-item { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; -} - -.KnownHosts-item__wrapper { - display: flex; - align-items: center; -} - -.KnownHosts-item__label { - position: relative; -} - -.KnownHosts-item__label svg { - display: none; - position: absolute; - left: -.1em; - top: 50%; - transform: translate(-100%, -50%); - font-size: .9em; -} - -.KnownHosts-item__status { - display: none; -} - -.KnownHosts-item__status svg { - margin-left: -5px; - margin-right: 5px; -} - -.KnownHosts-validation { - position: absolute; - top: 0; - right: 0; - transform: translateY(-100%); - font-weight: bold; -} - -.KnownHosts-error { - display: flex; - align-items: center; -} - -.KnownHosts-error svg { - margin-left: 4px; -} - -.KnownHosts .MuiSelect-select .KnownHosts-item__status { - display: flex; -} - -.Mui-selected .KnownHosts-item__label svg { - display: block; -} - -.MuiSelect-select .KnownHosts-item__edit { - display: none; -} diff --git a/webclient/src/components/KnownHosts/KnownHosts.i18n.json b/webclient/src/components/KnownHosts/KnownHosts.i18n.json deleted file mode 100644 index 3de8fd887..000000000 --- a/webclient/src/components/KnownHosts/KnownHosts.i18n.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "KnownHosts": { - "label": "Host", - "add": "Add new host", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - } -} diff --git a/webclient/src/components/KnownHosts/KnownHosts.tsx b/webclient/src/components/KnownHosts/KnownHosts.tsx deleted file mode 100644 index f65a98c38..000000000 --- a/webclient/src/components/KnownHosts/KnownHosts.tsx +++ /dev/null @@ -1,285 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import { styled } from '@mui/material/styles'; -import { useTranslation } from 'react-i18next'; -import { Select, MenuItem } from '@mui/material'; -import Button from '@mui/material/Button'; -import FormControl from '@mui/material/FormControl'; -import IconButton from '@mui/material/IconButton'; -import WifiTetheringIcon from '@mui/icons-material/WifiTethering'; -import PortableWifiOffIcon from '@mui/icons-material/PortableWifiOff'; -import InputLabel from '@mui/material/InputLabel'; -import Check from '@mui/icons-material/Check'; -import AddIcon from '@mui/icons-material/Add'; -import EditRoundedIcon from '@mui/icons-material/Edit'; -import ErrorOutlinedIcon from '@mui/icons-material/ErrorOutlined'; - -import { AuthenticationService } from 'api'; -import { KnownHostDialog } from 'dialogs'; -import { useReduxEffect } from 'hooks'; -import { HostDTO } from 'services'; -import { ServerTypes } from 'store'; -import { DefaultHosts, Host, getHostPort } from 'types'; -import Toast from 'components/Toast/Toast'; - -import './KnownHosts.css'; - -enum TestConnection { - TESTING = 'testing', - FAILED = 'failed', - SUCCESS = 'success', -} - -const PREFIX = 'KnownHosts'; - -const classes = { - root: `${PREFIX}-root` -}; - -const Root = styled('div')(({ theme }) => ({ - [`&.${classes.root}`]: { - '& .KnownHosts-error': { - color: theme.palette.error.main - }, - - '& .KnownHosts-warning': { - color: theme.palette.warning.main - }, - - '& .KnownHosts-item': { - [`& .${TestConnection.TESTING}`]: { - color: theme.palette.warning.main - }, - [`& .${TestConnection.FAILED}`]: { - color: theme.palette.error.main - }, - [`& .${TestConnection.SUCCESS}`]: { - color: theme.palette.success.main - } - } - } -})); - -const KnownHosts = (props) => { - const { input: { onChange }, meta, disabled } = props; - const { touched, error, warning } = meta; - - const { t } = useTranslation(); - - const [hostsState, setHostsState] = useState({ - hosts: [], - selectedHost: {} as any, - }); - - const [dialogState, setDialogState] = useState({ - open: false, - edit: null, - }); - - const [testingConnection, setTestingConnection] = useState(null); - - const [showCreateToast, setShowCreateToast] = useState(false); - const [showDeleteToast, setShowDeleteToast] = useState(false); - const [showEditToast, setShowEditToast] = useState(false); - - const loadKnownHosts = useCallback(async () => { - const hosts = await HostDTO.getAll(); - - if (!hosts?.length) { - // @TODO: find a better pattern to seeding default data in indexedDB - await HostDTO.bulkAdd(DefaultHosts); - loadKnownHosts(); - } else { - const selectedHost = hosts.find(({ lastSelected }) => lastSelected) || hosts[0]; - setHostsState(s => ({ ...s, hosts, selectedHost })); - } - }, []); - - useEffect(() => { - loadKnownHosts(); - }, [loadKnownHosts]); - - useEffect(() => { - const { hosts, selectedHost } = hostsState; - - if (selectedHost?.id) { - updateLastSelectedHost(selectedHost.id).then(() => { - onChange(selectedHost); - }); - } - }, [hostsState, onChange]); - - useReduxEffect(() => { - setTestingConnection(TestConnection.SUCCESS); - }, ServerTypes.TEST_CONNECTION_SUCCESSFUL, []); - - useReduxEffect(() => { - setTestingConnection(TestConnection.FAILED); - }, ServerTypes.TEST_CONNECTION_FAILED, []); - - const selectHost = (selectedHost) => { - setHostsState(s => ({ ...s, selectedHost })); - }; - - const openAddKnownHostDialog = () => { - setDialogState(s => ({ ...s, open: true, edit: null })); - }; - - const openEditKnownHostDialog = (host: HostDTO) => { - setDialogState(s => ({ ...s, open: true, edit: host })); - }; - - const closeKnownHostDialog = () => { - setDialogState(s => ({ ...s, open: false })); - } - - const handleDialogRemove = async ({ id }) => { - setHostsState(s => ({ - ...s, - hosts: s.hosts.filter(host => host.id !== id), - selectedHost: s.selectedHost.id === id ? s.hosts[0] : s.selectedHost, - })); - - closeKnownHostDialog(); - HostDTO.delete(id); - setShowDeleteToast(true) - }; - - const handleDialogSubmit = async ({ id, name, host, port }) => { - if (id) { - const hostDTO = await HostDTO.get(id); - hostDTO.name = name; - hostDTO.host = host; - hostDTO.port = port; - await hostDTO.save(); - - setHostsState(s => ({ - ...s, - hosts: s.hosts.map(h => h.id === id ? hostDTO : h), - selectedHost: hostDTO - })); - setShowEditToast(true) - } else { - const newHost: Host = { name, host, port, editable: true }; - newHost.id = await HostDTO.add(newHost) as number; - - setHostsState(s => ({ - ...s, - hosts: [...s.hosts, newHost], - selectedHost: newHost, - })); - setShowCreateToast(true) - } - - closeKnownHostDialog(); - }; - - const updateLastSelectedHost = (hostId): Promise => { - testConnection(); - - return HostDTO.getAll().then(hosts => - hosts.map(async host => { - if (host.id === hostId) { - host.lastSelected = true; - return await host.save(); - } - - if (host.lastSelected) { - host.lastSelected = false; - return await host.save(); - } - - return host; - }) - ); - }; - - const testConnection = () => { - setTestingConnection(TestConnection.TESTING); - - const options = { ...getHostPort(hostsState.selectedHost) }; - AuthenticationService.testConnection(options); - } - - return ( - - - { touched && ( -
- { - (error && -
- {error} - -
- ) || - - (warning &&
{warning}
) - } -
- ) } - - { t('KnownHosts.label') } - -
- - - setShowCreateToast(false)}>{ t('KnownHosts.toast', { mode: 'created' }) } - setShowDeleteToast(false)}>{ t('KnownHosts.toast', { mode: 'deleted' }) } - setShowEditToast(false)}>{ t('KnownHosts.toast', { mode: 'edited' }) } -
- ); -}; - -export default KnownHosts; diff --git a/webclient/src/components/LanguageDropdown/LanguageDropdown.css b/webclient/src/components/LanguageDropdown/LanguageDropdown.css deleted file mode 100644 index c4db3d0d0..000000000 --- a/webclient/src/components/LanguageDropdown/LanguageDropdown.css +++ /dev/null @@ -1,19 +0,0 @@ -.LanguageDropdown { -} - -.LanguageDropdown-item { - display: flex; - align-items: center; -} - -.LanguageDropdown-item__image { - width: 1.5em; -} - -.MuiSelect-select .LanguageDropdown-item__label { - display: none; -} - -.MuiList-root .LanguageDropdown-item__image { - margin-right: 1em; -} diff --git a/webclient/src/components/LanguageDropdown/LanguageDropdown.tsx b/webclient/src/components/LanguageDropdown/LanguageDropdown.tsx deleted file mode 100644 index 8f63bd549..000000000 --- a/webclient/src/components/LanguageDropdown/LanguageDropdown.tsx +++ /dev/null @@ -1,57 +0,0 @@ -// eslint-disable-next-line -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Select, MenuItem } from '@mui/material'; -import FormControl from '@mui/material/FormControl'; -import InputLabel from '@mui/material/InputLabel'; - -import { Images } from 'images/Images'; -import { Language, LanguageCountry, LanguageNative } from 'types'; - -import './LanguageDropdown.css'; - -const LanguageDropdown = () => { - const { t, i18n } = useTranslation(); - const [language, setLanguage] = useState(i18n.resolvedLanguage); - - useEffect(() => { - if (language !== i18n.resolvedLanguage) { - i18n.changeLanguage(language); - } - }, [language]); - - return ( - - - - ) -}; - -export default LanguageDropdown; diff --git a/webclient/src/components/Message/CardCallout.css b/webclient/src/components/Message/CardCallout.css deleted file mode 100644 index 0011b238a..000000000 --- a/webclient/src/components/Message/CardCallout.css +++ /dev/null @@ -1,4 +0,0 @@ -.callout { - font-weight: bold; - color: green; -} diff --git a/webclient/src/components/Message/CardCallout.tsx b/webclient/src/components/Message/CardCallout.tsx deleted file mode 100644 index d34316541..000000000 --- a/webclient/src/components/Message/CardCallout.tsx +++ /dev/null @@ -1,94 +0,0 @@ -// eslint-disable-next-line -import React, { useMemo, useState } from 'react'; -import { styled } from '@mui/material/styles'; -import Popover from '@mui/material/Popover'; - -import { CardDTO, TokenDTO } from 'services'; - -import CardDetails from '../CardDetails/CardDetails'; -import TokenDetails from '../TokenDetails/TokenDetails'; - -import './CardCallout.css'; - -const PREFIX = 'CardCallout'; - -const classes = { - popover: `${PREFIX}-popover`, - popoverContent: `${PREFIX}-popoverContent` -}; - -const Root = styled('span')(({ theme }) => ({ - [`& .${classes.popover}`]: { - pointerEvents: 'none', - }, - - [`& .${classes.popoverContent}`]: { - pointerEvents: 'none', - } -})); - -const CardCallout = ({ name }) => { - const [card, setCard] = useState(null); - const [token, setToken] = useState(null); - const [anchorEl, setAnchorEl] = useState(null); - - useMemo(async () => { - const card = await CardDTO.get(name); - if (card) { - return setCard(card) - } - - const token = await TokenDTO.get(name); - if (token) { - return setToken(token); - } - }, [name]); - - const handlePopoverOpen = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handlePopoverClose = () => { - setAnchorEl(null); - }; - - const open = Boolean(anchorEl); - - return ( - - {card?.name || token?.name?.value || name} - - { - (card || token) && ( - -
- { card && () } - { token && () } -
-
- ) - } -
- ); -}; - -export default CardCallout; diff --git a/webclient/src/components/Message/Message.css b/webclient/src/components/Message/Message.css deleted file mode 100644 index cbb0df2a9..000000000 --- a/webclient/src/components/Message/Message.css +++ /dev/null @@ -1,3 +0,0 @@ -.link { - color: blue; -} diff --git a/webclient/src/components/Message/Message.tsx b/webclient/src/components/Message/Message.tsx deleted file mode 100644 index ec0b05507..000000000 --- a/webclient/src/components/Message/Message.tsx +++ /dev/null @@ -1,105 +0,0 @@ -// eslint-disable-next-line -import React, { useEffect, useMemo, useState } from 'react'; - -import { NavLink, generatePath } from 'react-router-dom'; - -import { - RouteEnum, - URL_REGEX, - MESSAGE_SENDER_REGEX, - MENTION_REGEX, - CARD_CALLOUT_REGEX, - CALLOUT_BOUNDARY_REGEX, -} from 'types'; - -import CardCallout from './CardCallout'; -import './Message.css'; - -const Message = ({ message: { message, messageType, timeOf, timeReceived } }) => ( -
-
- -
-
-); - -const ParsedMessage = ({ message }) => { - const [messageChunks, setMessageChunks] = useState(null); - const [name, setName] = useState(null); - - useMemo(() => { - const name = message.match(MESSAGE_SENDER_REGEX); - - if (name) { - setName(name[1]); - } - - setMessageChunks(parseMessage(message)); - }, [message]); - - return ( -
- { name && (:) } - { messageChunks } -
- ); -}; - -const PlayerLink = ({ name, label = name }) => ( - - {label} - -); - -function parseMessage(message) { - return message.replace(MESSAGE_SENDER_REGEX, '') - .split(CARD_CALLOUT_REGEX) - .filter(chunk => !!chunk) - .map(parseChunks); -} - -function parseChunks(chunk, index) { - if (chunk.match(CARD_CALLOUT_REGEX)) { - const name = chunk.replace(CALLOUT_BOUNDARY_REGEX, '').trim(); - return (); - } - - if (chunk.match(URL_REGEX)) { - return parseUrlChunk(chunk); - } - - if (chunk.match(MENTION_REGEX)) { - return parseMentionChunk(chunk); - } - - return chunk; -} - -function parseUrlChunk(chunk) { - return chunk.split(URL_REGEX) - .filter(urlChunk => !!urlChunk) - .map((urlChunk, index) => { - if (urlChunk.match(URL_REGEX)) { - return ({urlChunk}); - } - - return urlChunk; - }); -} - -function parseMentionChunk(chunk) { - return chunk.split(MENTION_REGEX) - .filter(mentionChunk => !!mentionChunk) - .map((mentionChunk, index) => { - const mention = mentionChunk.match(MENTION_REGEX); - - if (mention) { - const name = mention[0].substr(1); - return (); - } - - return mentionChunk; - }); -} - -export default Message; diff --git a/webclient/src/components/ScrollToBottomOnChanges/ScrollToBottomOnChanges.tsx b/webclient/src/components/ScrollToBottomOnChanges/ScrollToBottomOnChanges.tsx deleted file mode 100644 index 939a7d753..000000000 --- a/webclient/src/components/ScrollToBottomOnChanges/ScrollToBottomOnChanges.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { useEffect, useRef } from 'react'; - -const ScrollToBottomOnChanges = ({ content, changes }) => { - const messagesEndRef = useRef(null); - - // @TODO (2) - const scrollToBottom = () => { - messagesEndRef.current.scrollIntoView({ behavior: 'smooth' }) - } - - useEffect(scrollToBottom, [changes]); - - const styling = { - height: '100%' - }; - - return ( -
- {content} -
-
- ) -} - -export default ScrollToBottomOnChanges; diff --git a/webclient/src/components/SelectField/SelectField.css b/webclient/src/components/SelectField/SelectField.css deleted file mode 100644 index c59e851ae..000000000 --- a/webclient/src/components/SelectField/SelectField.css +++ /dev/null @@ -1,4 +0,0 @@ -.select-field label { - background: white; - padding: 0 5px; -} \ No newline at end of file diff --git a/webclient/src/components/SelectField/SelectField.tsx b/webclient/src/components/SelectField/SelectField.tsx deleted file mode 100644 index fdbef0e9c..000000000 --- a/webclient/src/components/SelectField/SelectField.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import FormControl from '@mui/material/FormControl'; -import InputLabel from '@mui/material/InputLabel'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; - -import './SelectField.css'; - -const SelectField = ({ input, label, options, value }) => { - const id = label + '-select-field'; - const labelId = id + '-label'; - - return ( - - {label} - - - ); -}; - -export default SelectField; diff --git a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css deleted file mode 100644 index f439dee5c..000000000 --- a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.css +++ /dev/null @@ -1,37 +0,0 @@ -.three-pane-layout, -.three-pane-layout .grid { - width: 100%; - height: 100%; - margin: 0; -} - -.three-pane-layout .grid-main, -.three-pane-layout .grid-side { - height: 100%; -} - -.three-pane-layout .grid-main { - display: flex; - flex-direction: column; -} - -.three-pane-layout .grid-main__top { - max-height: 50%; - width: 100%; - padding-bottom: 20px; - flex-shrink: 0; -} - -.three-pane-layout .grid-main__bottom { - height: 100%; - width: 100%; - flex-shrink: 1; - overflow: hidden; -} - -.three-pane-layout .grid-main__top.fixedHeight, -.three-pane-layout .grid-main__bottom.fixedHeight { - height: 50%; - overflow: visible; - padding: 0 0 16px; -} diff --git a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx deleted file mode 100644 index 7919f9d35..000000000 --- a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Component, CElement } from 'react'; -import { connect } from 'react-redux'; -import Grid from '@mui/material/Grid'; -import Hidden from '@mui/material/Hidden'; - -import './ThreePaneLayout.css'; - -// @DEPRECATED -// This component sucks balls, dont use it. It will be removed sooner than later. -class ThreePaneLayout extends Component { - render() { - return ( -
- - - - {this.props.top} - - - {this.props.bottom} - - - - - {this.props.side} - - - -
- ); - } -} - -interface ThreePaneLayoutProps { - top: CElement, - bottom: CElement, - side?: CElement, - fixedHeight?: boolean, -} - -const mapStateToProps = state => ({}); - -export default connect(mapStateToProps)(ThreePaneLayout); diff --git a/webclient/src/components/Toast/Toast.tsx b/webclient/src/components/Toast/Toast.tsx deleted file mode 100644 index 4ef8a3cad..000000000 --- a/webclient/src/components/Toast/Toast.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import * as React from 'react' -import ReactDOM from 'react-dom' - -import Alert, { AlertProps } from '@mui/material/Alert'; -import CheckCircleIcon from '@mui/icons-material/CheckCircle'; -import Slide, { SlideProps } from '@mui/material/Slide'; -import Snackbar from '@mui/material/Snackbar'; - -const iconMapping = { - success: -} - -function Toast(props) { - const { open, onClose, severity, autoHideDuration, children } = props - - const rootElemRef = React.useRef(document.createElement('div')); - - React.useEffect(() => { - document.body.appendChild(rootElemRef.current) - return () => { - rootElemRef.current.remove(); - } - }, [rootElemRef]) - - const handleClose = (event?: React.SyntheticEvent, reason?: string) => { - if (reason === 'clickaway') { - return; - } - onClose(event); - }; - - const node = ( - - - {children} - - - ) - if (!rootElemRef.current) { - return null - } - - return ReactDOM.createPortal( - node, - rootElemRef.current - ); -} - -Toast.defaultProps = { - severity: 'success', - // 10s wait before automatically dismissing the Toast. - autoHideDuration: 10000, -} - -function TransitionLeft(props) { - return ; -} - -export default Toast diff --git a/webclient/src/components/Toast/ToastContext.tsx b/webclient/src/components/Toast/ToastContext.tsx deleted file mode 100644 index 44753d87f..000000000 --- a/webclient/src/components/Toast/ToastContext.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { createContext, FC, PropsWithChildren, ReactChild, ReactNode, useContext, useEffect, useReducer, ContextType, Context } from 'react' - -import { ACTIONS, initialState, reducer } from './reducer'; -import Toast from './Toast' - -interface ToastEntry { - isOpen: boolean, - children: ReactChild, -} - -interface ToastState { - toasts: Map, - addToast: (key, children) => void, - openToast: (key) => void, - closeToast: (key) => void, - removeToast: (key) => void, -} - -const ToastContext: Context = createContext({ - toasts: new Map(), - addToast: (key, children) => {}, - openToast: (key) => {}, - closeToast: (key) => {}, - removeToast: (key) => {}, -}); - -export const ToastProvider: FC = (props) => { - const { children } = props - const [state, dispatch] = useReducer(reducer, initialState) - const providerState = { - toasts: state.toasts, - addToast: (key, children) => dispatch({ type: ACTIONS.ADD_TOAST, payload: { key, children } }), - openToast: key => dispatch({ type: ACTIONS.OPEN_TOAST, payload: { key } }), - closeToast: key => dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } }), - removeToast: key => dispatch({ type: ACTIONS.REMOVE_TOAST, payload: { key } }), - } - return ( - - {children} -
- {Array.from(state.toasts).map(([key, value]) => { - const { isOpen, children } = value; - return ( - dispatch({ type: ACTIONS.CLOSE_TOAST, payload: { key } })}> - {children} - - ) - })} -
-
- ) -} - -export interface ToastHookOptions { - key: string, - children: ReactNode -} - -export function useToast({ key, children }) { - const { addToast, openToast, closeToast, removeToast } = useContext(ToastContext) - - useEffect(() => { - addToast(key, children) - }, []) - - return { - openToast: () => openToast(key), - closeToast: () => closeToast(key), - removeToast: () => removeToast(key), - } -} diff --git a/webclient/src/components/Toast/index.ts b/webclient/src/components/Toast/index.ts deleted file mode 100644 index 40e7c7368..000000000 --- a/webclient/src/components/Toast/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useToast, ToastProvider } from './ToastContext'; -import Toast from './Toast'; - -export { - Toast as default, - useToast, - ToastProvider, -} diff --git a/webclient/src/components/Toast/reducer.ts b/webclient/src/components/Toast/reducer.ts deleted file mode 100644 index 3f600db52..000000000 --- a/webclient/src/components/Toast/reducer.ts +++ /dev/null @@ -1,61 +0,0 @@ -export const ACTIONS = { - ADD_TOAST: 'ADD_TOAST', - OPEN_TOAST: 'OPEN_TOAST', - CLOSE_TOAST: 'CLOSE_TOAST', - REMOVE_TOAST: 'REMOVE_TOAST', -} - -export const initialState = { - toasts: {} -} - -export function reducer(state, { type, payload }) { - const { key, children } = payload; - - switch (type) { - case ACTIONS.ADD_TOAST: { - return { - ...state, - toasts: { - ...state.toasts, - [key]: { - isOpen: false, - children, - }, - }, - }; - } - case ACTIONS.OPEN_TOAST: { - return { - ...state, - toasts: { - ...state.toasts, - [key]: { - ...state.toasts[key], - isOpen: true, - }, - }, - }; - } - case ACTIONS.CLOSE_TOAST: { - return { - ...state, - toasts: { - ...state.toasts, - [key]: { - ...state.toasts[key], - isOpen: false, - }, - }, - }; - } - case ACTIONS.REMOVE_TOAST: { - const newState = { ...state }; - delete newState.toasts[key]; - - return newState; - } - default: - throw Error('Please pick an available action') - } -} diff --git a/webclient/src/components/Token/Token.css b/webclient/src/components/Token/Token.css deleted file mode 100644 index 08f18b872..000000000 --- a/webclient/src/components/Token/Token.css +++ /dev/null @@ -1,4 +0,0 @@ -.token { - width: 100%; - height: 100%; -} diff --git a/webclient/src/components/Token/Token.tsx b/webclient/src/components/Token/Token.tsx deleted file mode 100644 index 29b39ecc5..000000000 --- a/webclient/src/components/Token/Token.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// eslint-disable-next-line -import React, { useMemo, useState } from 'react'; - -import { TokenDTO } from 'services'; - -import './Token.css'; - -interface TokenProps { - token: TokenDTO; -} - -const Token = ({ token }: TokenProps) => { - const set = Array.isArray(token?.set) ? token?.set[0] : token?.set; - return token && ( - {token?.name?.value} - ); -} - -export default Token; diff --git a/webclient/src/components/TokenDetails/TokenDetails.css b/webclient/src/components/TokenDetails/TokenDetails.css deleted file mode 100644 index 3c3267839..000000000 --- a/webclient/src/components/TokenDetails/TokenDetails.css +++ /dev/null @@ -1,46 +0,0 @@ -.tokenDetails { - padding: 10px; - width: calc(400px * .716); - font-size: 10px; -} - -.tokenDetails-token { - height: 400px; - margin: 0 auto; -} - -.tokenDetails-attribute { - display: flex; - justify-content: space-between; - align-items: baseline; -} - -.tokenDetails-attributes { - margin-top: 10px; -} - -.tokenDetails-attribute__label { - text-transform: uppercase; - font-size: 10px; - margin-right: 10px; -} - -.tokenDetails-attribute__value { - text-align: right; -} - -.tokenDetails-text { - font-size: 12px; - margin-top: 10px; - padding: 5px; - background: rgba(0, 0, 0, .15); - white-space: pre-line; -} - -.tokenDetails-text__flavor { - font-style: italic; -} - -.tokenDetails-text__current:not(:empty) + .tokenDetails-text__flavor { - margin-top: 10px; -} diff --git a/webclient/src/components/TokenDetails/TokenDetails.tsx b/webclient/src/components/TokenDetails/TokenDetails.tsx deleted file mode 100644 index f3d8aed96..000000000 --- a/webclient/src/components/TokenDetails/TokenDetails.tsx +++ /dev/null @@ -1,86 +0,0 @@ -// eslint-disable-next-line -import React, { useMemo, useState } from 'react'; - -import { TokenDTO } from 'services'; - -import Token from '../Token/Token'; - -import './TokenDetails.css'; - -interface TokenProps { - token: TokenDTO; -} - -const TokenDetails = ({ token }: TokenProps) => { - const props = token?.prop?.value; - - return ( -
-
- -
- - { - token && ( -
-
-
- Name: - {token.name?.value} -
- - { - (!props.pt?.value) ? null : ( -
- P/T: - {props.pt.value} -
- ) - } - - { - !props.colors?.value ? null : ( -
- Color(s): - {props.colors.value} -
- ) - } - - { - !props.maintype?.value ? null : ( -
- Main Type: - {props.maintype.value} -
- ) - } - - { - !props.type?.value ? null : ( -
- Type: - {props.type.value} -
- ) - } -
- - { - !token.text?.value ? null : ( -
-
- {token.text.value} -
-
- ) - } -
- ) - } - -
- ); -} - -export default TokenDetails; diff --git a/webclient/src/components/UserDisplay/UserDisplay.css b/webclient/src/components/UserDisplay/UserDisplay.css deleted file mode 100644 index 0697c7f68..000000000 --- a/webclient/src/components/UserDisplay/UserDisplay.css +++ /dev/null @@ -1,16 +0,0 @@ -.user-display, -.user-display__link { - height: 100%; - width: 100%; -} - -.user-display__details { - height: 100%; - display: flex; - align-items: center; -} - -.user-display__country { - width: 1.1em; - margin-right: 0.4em; -} diff --git a/webclient/src/components/UserDisplay/UserDisplay.tsx b/webclient/src/components/UserDisplay/UserDisplay.tsx deleted file mode 100644 index 9c45a0f51..000000000 --- a/webclient/src/components/UserDisplay/UserDisplay.tsx +++ /dev/null @@ -1,150 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import { NavLink, generatePath } from 'react-router-dom'; - -import Menu from '@mui/material/Menu'; -import MenuItem from '@mui/material/MenuItem'; - -import { Images } from 'images/Images'; -import { SessionService } from 'api'; -import { ServerSelectors } from 'store'; -import { RouteEnum, User } from 'types'; - -import './UserDisplay.css'; - - -class UserDisplay extends Component { - constructor(props) { - super(props); - - this.handleClick = this.handleClick.bind(this); - this.handleClose = this.handleClose.bind(this); - this.navigateToUserProfile = this.navigateToUserProfile.bind(this); - this.addToBuddyList = this.addToBuddyList.bind(this); - this.removeFromBuddyList = this.removeFromBuddyList.bind(this); - this.addToIgnoreList = this.addToIgnoreList.bind(this); - this.removeFromIgnoreList = this.removeFromIgnoreList.bind(this); - - this.isABuddy = this.isABuddy.bind(this); - this.isIgnored = this.isIgnored.bind(this); - - this.state = { - position: null - }; - } - - handleClick(event) { - event.preventDefault(); - - this.setState({ - position: { - x: event.clientX + 2, - y: event.clientY + 4, - } - }); - } - - handleClose() { - this.setState({ - position: null - }); - } - - navigateToUserProfile() { - this.handleClose(); - } - - addToBuddyList() { - SessionService.addToBuddyList(this.props.user.name); - this.handleClose(); - } - - removeFromBuddyList() { - SessionService.removeFromBuddyList(this.props.user.name); - this.handleClose(); - } - - addToIgnoreList() { - SessionService.addToIgnoreList(this.props.user.name); - this.handleClose(); - } - - removeFromIgnoreList() { - SessionService.removeFromIgnoreList(this.props.user.name); - this.handleClose(); - } - - isABuddy() { - return this.props.buddyList.filter(user => user.name === this.props.user.name).length; - } - - isIgnored() { - return this.props.ignoreList.filter(user => user.name === this.props.user.name).length; - } - - render() { - const { user } = this.props; - const { position } = this.state; - const { name, country } = user; - - const isABuddy = this.isABuddy(); - const isIgnored = this.isIgnored(); - - // console.log('user', name, !!isABuddy, !!isIgnored); - - return ( -
- -
- {country} -
{name}
-
-
-
- - - Chat - - { - !isABuddy - ? (Add to Buddy List) - : (Remove From Buddy List) - } - { - !isIgnored - ? (Add to Ignore List) - : (Remove From Ignore List) - } - -
-
- ); - } -} - -interface UserDisplayProps { - user: User; - buddyList: User[]; - ignoreList: User[]; -} - -interface UserDisplayState { - position: any; -} - -const mapStateToProps = (state) => ({ - buddyList: ServerSelectors.getBuddyList(state), - ignoreList: ServerSelectors.getIgnoreList(state) -}); - -export default connect(mapStateToProps)(UserDisplay); diff --git a/webclient/src/components/VirtualList/VirtualList.css b/webclient/src/components/VirtualList/VirtualList.css deleted file mode 100644 index 330cd5a90..000000000 --- a/webclient/src/components/VirtualList/VirtualList.css +++ /dev/null @@ -1,3 +0,0 @@ -.virtual-list { - height: 100%; -} \ No newline at end of file diff --git a/webclient/src/components/VirtualList/VirtualList.tsx b/webclient/src/components/VirtualList/VirtualList.tsx deleted file mode 100644 index e40ef712c..000000000 --- a/webclient/src/components/VirtualList/VirtualList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -// eslint-disable-next-line -import React from "react"; - -import { FixedSizeList as List } from 'react-window'; -import AutoSizer from 'react-virtualized-auto-sizer'; - -import './VirtualList.css'; - -const VirtualList = ({ items, itemKey, className = {}, size = 30 }) => ( -
- - {({ height, width }) => ( - - {Row} - - )} - -
-); - -const Row = ({ data, index, style }) => ( -
- {data[index]} -
-); - -export default VirtualList; diff --git a/webclient/src/components/index.ts b/webclient/src/components/index.ts deleted file mode 100644 index 2d67b3003..000000000 --- a/webclient/src/components/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Common components -export { default as Card } from './Card/Card'; -export { default as CardDetails } from './CardDetails/CardDetails'; -export { default as CountryDropdown } from './CountryDropdown/CountryDropdown'; -export { default as InputField } from './InputField/InputField'; -export { default as InputAction } from './InputAction/InputAction'; -export { default as KnownHosts } from './KnownHosts/KnownHosts'; -export { default as LanguageDropdown } from './LanguageDropdown/LanguageDropdown'; -export { default as Message } from './Message/Message'; -export { default as VirtualList } from './VirtualList/VirtualList'; -export { default as UserDisplay } from './UserDisplay/UserDisplay'; -export { default as ThreePaneLayout } from './ThreePaneLayout/ThreePaneLayout'; -export { default as CheckboxField } from './CheckboxField/CheckboxField'; -export { default as SelectField } from './SelectField/SelectField'; -export { default as ScrollToBottomOnChanges } from './ScrollToBottomOnChanges/ScrollToBottomOnChanges'; - -// Guards -export { default as AuthGuard } from './Guard/AuthGuard'; -export { default as ModGuard } from './Guard/ModGuard'; diff --git a/webclient/src/containers/Account/Account.css b/webclient/src/containers/Account/Account.css deleted file mode 100644 index 6b5c995a9..000000000 --- a/webclient/src/containers/Account/Account.css +++ /dev/null @@ -1,53 +0,0 @@ -.account { - display: flex; - justify-content: space-between; - height: 100%; - padding: 5px; -} - -.account-column { - display: flex; - flex-direction: column; - width: 33%; -} - - -.account-list { - display: flex; - flex-direction: column; - height: 100%; - padding: 20px; -} - -.account-details { - display: flex; - flex-direction: column; - align-items: center; - padding: 20px; -} - - -.account-details__actions { - display: flex; - align-items: stretch; - justify-content: space-around; - width: 100%; -} - -.account-details p { - margin-bottom: 10px; -} - -.account-details button { - margin-top: 10px; - font-size: 10px; -} - -.account-details > img { - width: 100%; - margin-bottom: 20px; -} - -.account-details__lang { - margin-top: 20px; -} diff --git a/webclient/src/containers/Account/Account.tsx b/webclient/src/containers/Account/Account.tsx deleted file mode 100644 index 0c4e001a0..000000000 --- a/webclient/src/containers/Account/Account.tsx +++ /dev/null @@ -1,120 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { useTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; - -import Button from '@mui/material/Button'; -import ListItem from '@mui/material/ListItem'; -import Paper from '@mui/material/Paper'; - -import { UserDisplay, VirtualList, AuthGuard, LanguageDropdown } from 'components'; -import { AuthenticationService, SessionService } from 'api'; -import { ServerSelectors } from 'store'; -import { User } from 'types'; -import Layout from 'containers/Layout/Layout'; - -import AddToBuddies from './AddToBuddies'; -import AddToIgnore from './AddToIgnore'; - -import './Account.css'; - -const Account = (props: AccountProps) => { - const { buddyList, ignoreList, serverName, serverVersion, user } = props; - const { country, realName, name, userLevel, accountageSecs, avatarBmp } = user || {}; - let url = URL.createObjectURL(new Blob([avatarBmp], { 'type': 'image/png' })); - - const { t } = useTranslation(); - - const handleAddToBuddies = ({ userName }) => { - SessionService.addToBuddyList(userName); - }; - - const handleAddToIgnore = ({ userName }) => { - SessionService.addToIgnoreList(userName); - }; - - return ( - - -
- -
- Buddies Online: ?/{buddyList.length} -
- buddyList[index].name } - items={ buddyList.map(user => ( - - - - )) } - /> -
- -
-
-
-
- -
- Ignored Users Online: ?/{ignoreList.length} -
- ignoreList[index].name } - items={ ignoreList.map(user => ( - - - - )) } - /> -
- -
-
-
-
- - {name} -

{name}

-

Location: ({country?.toUpperCase()})

-

User Level: {userLevel}

-

Account Age: {accountageSecs}

-

Real Name: {realName}

-
- - - -
- -
- -

Server Name: {serverName}

-

Server Version: {serverVersion}

- - -
- -
-
-
-
- ) -} - -interface AccountProps { - buddyList: User[]; - ignoreList: User[]; - serverName: string; - serverVersion: string; - user: User; -} - -const mapStateToProps = state => ({ - buddyList: ServerSelectors.getBuddyList(state), - ignoreList: ServerSelectors.getIgnoreList(state), - serverName: ServerSelectors.getName(state), - serverVersion: ServerSelectors.getVersion(state), - user: ServerSelectors.getUser(state), -}); - -export default connect(mapStateToProps)(Account); diff --git a/webclient/src/containers/Account/AddToBuddies.tsx b/webclient/src/containers/Account/AddToBuddies.tsx deleted file mode 100644 index 3aa5489ad..000000000 --- a/webclient/src/containers/Account/AddToBuddies.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Form } from 'react-final-form' - -import { InputAction } from 'components'; - -const AddToBuddies = ({ onSubmit }) => ( -
onSubmit(values)}> - {({ handleSubmit }) => ( - - - - )} - -); - -export default AddToBuddies; diff --git a/webclient/src/containers/Account/AddToIgnore.tsx b/webclient/src/containers/Account/AddToIgnore.tsx deleted file mode 100644 index 270036946..000000000 --- a/webclient/src/containers/Account/AddToIgnore.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Form } from 'react-final-form' - -import { InputAction } from 'components'; - -const AddToIgnore = ({ onSubmit }) => ( -
onSubmit(values)}> - {({ handleSubmit }) => ( - - - - )} - -); - -export default AddToIgnore; diff --git a/webclient/src/containers/App/AppShell.css b/webclient/src/containers/App/AppShell.css deleted file mode 100644 index 4492a6bcf..000000000 --- a/webclient/src/containers/App/AppShell.css +++ /dev/null @@ -1,10 +0,0 @@ -.AppShell, -.AppShell-routes { - height: 100%; -} - -.AppShell { - display: flex; - flex-direction: column; - min-width: 768px; -} \ No newline at end of file diff --git a/webclient/src/containers/App/AppShell.tsx b/webclient/src/containers/App/AppShell.tsx deleted file mode 100644 index c28d20067..000000000 --- a/webclient/src/containers/App/AppShell.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, Suspense } from 'react'; -import { Provider } from 'react-redux'; -import { MemoryRouter as Router } from 'react-router-dom'; -import CssBaseline from '@mui/material/CssBaseline'; -import { store } from 'store'; -import Routes from './AppShellRoutes'; -import FeatureDetection from './FeatureDetection'; - -import './AppShell.css'; - -import { ToastProvider } from 'components/Toast' - -class AppShell extends Component { - componentDidMount() { - // @TODO (1) - window.onbeforeunload = () => true; - } - - handleContextMenu(event) { - event.preventDefault(); - } - - render() { - return ( - - - - -
- - - - -
-
-
-
- ); - } -} - -export default AppShell; diff --git a/webclient/src/containers/App/AppShellRoutes.tsx b/webclient/src/containers/App/AppShellRoutes.tsx deleted file mode 100644 index c80f74c30..000000000 --- a/webclient/src/containers/App/AppShellRoutes.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { Navigate, Route, Routes } from 'react-router-dom'; - -import { RouteEnum } from 'types'; -import { - Account, - Decks, - Game, - Player, - Room, - Server, - Login, - Logs, - Initialize, - Unsupported -} from 'containers'; - -const AppShellRoutes = () => ( -
- - } /> - - } /> - } /> - } /> - } /> - } /> - {} />} - } /> - } /> - } /> - -
-); - -export default AppShellRoutes; diff --git a/webclient/src/containers/App/FeatureDetection.tsx b/webclient/src/containers/App/FeatureDetection.tsx deleted file mode 100644 index ed9e1f241..000000000 --- a/webclient/src/containers/App/FeatureDetection.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Navigate } from 'react-router-dom'; -import { dexieService } from 'services'; -import { RouteEnum } from 'types'; - -const FeatureDetection = () => { - const [unsupported, setUnsupported] = useState(false); - - useEffect(() => { - const features: Promise[] = [ - detectIndexedDB(), - ]; - - Promise.all(features).catch((e) => setUnsupported(true)); - }, []); - - return unsupported - ? - : <>; - - function detectIndexedDB() { - return dexieService.testConnection(); - } -}; - -export default FeatureDetection; diff --git a/webclient/src/containers/Decks/Decks.css b/webclient/src/containers/Decks/Decks.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/webclient/src/containers/Decks/Decks.tsx b/webclient/src/containers/Decks/Decks.tsx deleted file mode 100644 index 190196dc9..000000000 --- a/webclient/src/containers/Decks/Decks.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; - -import { AuthGuard } from 'components/index'; -import Layout from 'containers/Layout/Layout'; - -import './Decks.css'; - -class Decks extends Component { - render() { - return ( - - - "Decks" - - ) - } -} - -export default Decks; diff --git a/webclient/src/containers/Game/Game.css b/webclient/src/containers/Game/Game.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/webclient/src/containers/Game/Game.tsx b/webclient/src/containers/Game/Game.tsx deleted file mode 100644 index 694ffb46d..000000000 --- a/webclient/src/containers/Game/Game.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; - -import { AuthGuard } from 'components'; -import Layout from 'containers/Layout/Layout'; - -import './Game.css'; - -class Game extends Component { - render() { - return ( - - - "Game" - - ) - } -} - -export default Game; diff --git a/webclient/src/containers/Initialize/Initialize.css b/webclient/src/containers/Initialize/Initialize.css deleted file mode 100644 index d52eafd5f..000000000 --- a/webclient/src/containers/Initialize/Initialize.css +++ /dev/null @@ -1,88 +0,0 @@ -.Initialize { - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - text-align: center; -} - -.Initialize img { - width: 60px; -} - -h6.subtitle { - margin: 20px 0 10px; -} - -.Initialize-graphics { - position: absolute; - height: 100%; - width: 100%; - overflow: hidden; -} - -.Initialize-graphics__square { - position: absolute; - border: 2px solid; - opacity: .05; -} - -.Initialize-graphics__bar { - position: absolute; - opacity: .05; - border-radius: 8px; -} - -.Initialize-graphics__square.topLeft { - transform: rotate(27deg); - top: 38px; - left: 64px; - height: 134px; - width: 100px; - border-radius: 8px; -} - -.Initialize-graphics__square.topRight { - transform: rotate(10deg); - top: 74px; - right: 62px; - height: 50px; - width: 66px; - border-radius: 20px; -} - -.Initialize-graphics__square.bottomLeft { - transform: rotate(120deg); - bottom: 61px; - left: 66px; - height: 50px; - width: 66px; - border-radius: 20px; -} - -.Initialize-graphics__square.bottomRight { - transform: rotate(-24deg); - bottom: 54px; - right: 0; - height: 88px; - width: 66px; - border-radius: 8px; -} - -.Initialize-graphics__bar.bottomBar { - transform: rotate(30deg); - bottom: -4px; - left: -29px; - height: 50px; - width: 222px; -} - -.Initialize-graphics__bar.topBar { - transform: rotate(-330deg); - top: 10px; - right: -49px; - height: 50px; - width: 222px; -} diff --git a/webclient/src/containers/Initialize/Initialize.i18n.json b/webclient/src/containers/Initialize/Initialize.i18n.json deleted file mode 100644 index 77ca5e8cd..000000000 --- a/webclient/src/containers/Initialize/Initialize.i18n.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "InitializeContainer": { - "title": "DID YOU KNOW", - "subtitle": "<1>Cockatrice is run by volunteers<1>that love card games!" - } -} diff --git a/webclient/src/containers/Initialize/Initialize.tsx b/webclient/src/containers/Initialize/Initialize.tsx deleted file mode 100644 index bc777f065..000000000 --- a/webclient/src/containers/Initialize/Initialize.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useState } from 'react'; -import { styled } from '@mui/material/styles'; -import { useTranslation, Trans } from 'react-i18next'; -import { connect } from 'react-redux'; -import { Navigate } from 'react-router-dom'; -import Typography from '@mui/material/Typography'; - -import { Images } from 'images'; -import { ServerSelectors } from 'store'; -import { RouteEnum } from 'types'; -import Layout from 'containers/Layout/Layout'; - -import './Initialize.css'; - -const PREFIX = 'Initialize'; - -const classes = { - root: `${PREFIX}-root` -}; - -const Root = styled('div')(({ theme }) => ({ - [`&.${classes.root}`]: { - '& .Initialize-graphics': { - color: theme.palette.primary.contrastText, - }, - - '& .Initialize-graphics__bar': { - backgroundColor: theme.palette.primary.contrastText, - }, - } -})); - -const Initialize = ({ initialized }: InitializeProps) => { - const { t } = useTranslation(); - - return initialized - ? - : ( - - -
- logo - { t('InitializeContainer.title') } - - - - -
- -
-
-
-
-
-
-
-
- - - ); -} - -interface InitializeProps { - initialized: boolean; -} - -const mapStateToProps = state => ({ - initialized: ServerSelectors.getInitialized(state), -}); - -export default connect(mapStateToProps)(Initialize); diff --git a/webclient/src/containers/Layout/Layout.css b/webclient/src/containers/Layout/Layout.css deleted file mode 100644 index a98cba47c..000000000 --- a/webclient/src/containers/Layout/Layout.css +++ /dev/null @@ -1,31 +0,0 @@ -.layout { - height: 100%; - max-height: 100%; - width: 100%; - max-width: 100%; - display: flex; - flex-flow: row nowrap; - overflow: hidden; -} - -.layout--no-height-limit { - height: initial; - max-height: initial; -} - -.bottom-bar__container { - background: #555; - height: 50px; - width: 100%; -} - -.page__body { - flex: 1; - max-height: calc(100% - 50px); -} - -.page { - display: flex; - flex-flow: column; - width: 100%; -} diff --git a/webclient/src/containers/Layout/Layout.tsx b/webclient/src/containers/Layout/Layout.tsx deleted file mode 100644 index 6d04172ba..000000000 --- a/webclient/src/containers/Layout/Layout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import LeftNav from './LeftNav'; - -import './Layout.css' - -function Layout(props:LayoutProps) { - const { children, className, showNav = true, noHeightLimit = false } = props; - const containerClasses = ['layout'] - if (noHeightLimit === true) { - containerClasses.push('layout--no-height-limit') - } - - return ( -
- {showNav && } -
-
- {children} -
- {showNav && } -
-
- ) -} - -function BottomBar(props) { - return ( -
-
- ) -} - -interface LayoutProps { - showNav?: boolean; - children: any; - className?: string; - noHeightLimit?: boolean -} - -export default Layout; diff --git a/webclient/src/containers/Layout/LeftNav.css b/webclient/src/containers/Layout/LeftNav.css deleted file mode 100644 index 85c851d2e..000000000 --- a/webclient/src/containers/Layout/LeftNav.css +++ /dev/null @@ -1,128 +0,0 @@ -.LeftNav__container { - background: #7033DB; - width: 100px; - min-width: 100px; - height: 100%; -} - -.LeftNav__logo { - display: flex; - align-items: center; - justify-content: center; - padding: 16px 0; -} - -.LeftNav__logo a { - line-height: 1; -} - -.LeftNav__logo img { - height: 32px; -} - -.LeftNav-content { - color: white; -} - -.LeftNav-serverDetails { - font-size: 12px; -} - -.LeftNav-server__indicator { - display: inline-block; - height: 12px; - width: 12px; - background: red; - border: 1px solid; - border-radius: 50%; - margin-left: 10px; -} - -.LeftNav-nav { -} - -.LeftNav-nav__links { - display: flex; - flex-flow: column; - align-items: center; - gap: 16px; -} - -.LeftNav-nav__link { - position: relative; - height: 100%; -} - -.LeftNav-nav__link:hover { - background: rgba(0, 0, 0, .125); -} - -.LeftNav-nav__link:hover .LeftNav-nav__link-menu { - display: block; -} - -.LeftNav-nav__link-btn { - display: flex; - height: 100%; - width: 100%; - align-items: center; - padding: 5px 20px; - font-weight: bold; -} - -.LeftNav-nav__link-btn__icon { - margin-left: 5px; -} - -.LeftNav-nav__link-menu { - display: none; - position: absolute; - bottom: 0; - transform: translateY(100%); - min-width: 150px; - background: #3f51b5; - box-shadow: 1px 1px 2px 0px black; - z-index: 1; -} - -.LeftNav-nav__link-menu__item { - padding: 0 !important; -} - -.LeftNav-nav__link-menu__btn { - padding: 6px 16px; - width: 100%; - color: white; - display: flex; - justify-content: space-between; -} - -.LeftNav-nav__actions { - display: flex; - justify-content: center; -} - -.LeftNav-nav__action { - -} - -.LeftNav-nav__action button { - color: white; -} - -.temp-subnav__rooms { - display: flex; - align-items: center; - font-size: 10px; - padding: 5px; -} - -.temp-chip { - margin-left: 5px; - text-decoration: none; -} - - -.temp-chip > div { - cursor: inherit; -} diff --git a/webclient/src/containers/Layout/LeftNav.tsx b/webclient/src/containers/Layout/LeftNav.tsx deleted file mode 100644 index a81eb6ce2..000000000 --- a/webclient/src/containers/Layout/LeftNav.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { NavLink, useNavigate, generatePath } from 'react-router-dom'; -import IconButton from '@mui/material/IconButton'; -import Menu from '@mui/material/Menu'; -import MenuItem from '@mui/material/MenuItem'; -import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import CloseIcon from '@mui/icons-material/Close'; -import MailOutlineRoundedIcon from '@mui/icons-material/MailOutline'; -import MenuRoundedIcon from '@mui/icons-material/MenuRounded'; -import * as _ from 'lodash'; - -import { AuthenticationService, RoomsService } from 'api'; -import { CardImportDialog } from 'dialogs'; -import { Images } from 'images'; -import { RoomsSelectors, ServerSelectors } from 'store'; -import { Room, RouteEnum, User } from 'types'; - -import './LeftNav.css'; - -const LeftNav = ({ joinedRooms, serverState, user }: LeftNavProps) => { - const navigate = useNavigate(); - const [state, setState] = useState({ - anchorEl: null, - showCardImportDialog: false, - options: [], - }); - - useEffect(() => { - let options: string[] = [ - 'Account', - 'Replays', - ]; - - if (user && AuthenticationService.isModerator(user)) { - options = [ - ...options, - 'Administration', - 'Logs' - ]; - } - - setState(s => ({ ...s, options })); - }, [user]); - - const handleMenuOpen = (event) => { - setState(s => ({ ...s, anchorEl: event.target })); - } - - const handleMenuItemClick = (option: string) => { - const route = RouteEnum[option.toUpperCase()]; - navigate(generatePath(route)); - } - - const handleMenuClose = () => { - setState(s => ({ ...s, anchorEl: null })); - } - - const leaveRoom = (event, roomId) => { - event.preventDefault(); - RoomsService.leaveRoom(roomId); - }; - - const openImportCardWizard = () => { - setState(s => ({ ...s, showCardImportDialog: true })); - handleMenuClose(); - } - - const closeImportCardWizard = () => { - setState(s => ({ ...s, showCardImportDialog: false })); - } - - return ( -
-
-
- - logo - - { AuthenticationService.isConnected(serverState) && ( - - ) } -
- { AuthenticationService.isConnected(serverState) && ( -
- -
- ) } -
- - -
- ); -} - -interface LeftNavProps { - serverState: number; - server: string; - user: User; - joinedRooms: Room[]; - showNav?: boolean; -} - -interface LeftNavState { - anchorEl: Element; - showCardImportDialog: boolean; - options: string[]; -} - -const mapStateToProps = state => ({ - serverState: ServerSelectors.getState(state), - server: ServerSelectors.getName(state), - user: ServerSelectors.getUser(state), - joinedRooms: RoomsSelectors.getJoinedRooms(state), -}); - -export default connect(mapStateToProps)(LeftNav); diff --git a/webclient/src/containers/Layout/logo.png b/webclient/src/containers/Layout/logo.png deleted file mode 100644 index 7ce83bd20..000000000 Binary files a/webclient/src/containers/Layout/logo.png and /dev/null differ diff --git a/webclient/src/containers/Login/Login.css b/webclient/src/containers/Login/Login.css deleted file mode 100644 index 7166187ad..000000000 --- a/webclient/src/containers/Login/Login.css +++ /dev/null @@ -1,202 +0,0 @@ -.login { - height: 100%; - padding: 50px; -} - -.login__wrapper { - display: flex; - flex-direction: column; - align-items: center; -} - -.login-content { - width: 100%; - max-width: 500px; - display: flex; - border-radius: 8px; - overflow: hidden; -} - -.login-content__header { - font-family: 'Teko', sans-serif; - font-size: 34px; - font-weight: bold; - display: flex; - align-items: center; - margin-bottom: 20px; -} - -.login-content__header img { - height: 60px; - margin-right: 15px; -} - -.login-content__form { - width: 100%; - padding: 50px 50px 33px; -} - -.login-content__form h1 { - margin-bottom: 20px; -} - -.login-form { - margin-top: 30px; -} - -.login-content__description { - display: none; - position: relative; - justify-content: center; - align-items: center; - text-align: center; - font-size: 24px; - overflow: hidden; -} - -.login-content__description-wrapper { - position: relative; - width: 70%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.login-content__description-cards { - width: 100%; - position: relative; - display: flex; - justify-content: space-between; -} - -.login-content__description-cards__card { - position: relative; - width: 34%; - padding-bottom: 46%; - border-radius: 8px; - box-shadow: 0 5px 10px 2px rgba(0,0,0,0.20); - font-weight: bold; - font-size: 16px; -} - -.login-content__description-cards__card-wrapper { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.login-content__description-cards__card img { - width: 70%; - border-radius: 50%; - margin: 21% 0 9%; -} - -.login-content__description-cards__card.leftCard { - transform: rotate(-12deg); -} - -.login-content__description-cards__card.rightCard { - transform: rotate(12deg); -} - -.login-content__description-cards__card.topCard { - width: 44%; - padding-bottom: 59%; - position: absolute; - top: 45%; - left: 50%; - transform: translate(-50%, -50%); -} - -.login-content__description-subtitle1 { - margin: 40px 0 20px; - font-size: 28px; - font-weight: bold; - -} -.login-content__description-subtitle2 { - font-size: 14px; -} -.login-content__description-square { - position: absolute; - border: 1px solid; - opacity: .1; -} -.login-content__description-bar { - position: absolute; - opacity: .1; - border-radius: 8px; -} - - -.login-content__description-square.topLeft { - transform: rotate(27deg); - top: 38px; - left: 64px; - height: 134px; - width: 100px; - border-radius: 8px; -} - -.login-content__description-square.topRight { - transform: rotate(10deg); - top: 74px; - right: 62px; - height: 50px; - width: 66px; - border-radius: 20px; -} - -.login-content__description-square.bottomLeft { - transform: rotate(120deg); - bottom: 61px; - left: 66px; - height: 50px; - width: 66px; - border-radius: 20px; -} - -.login-content__description-square.bottomRight { - transform: rotate(-24deg); - bottom: 54px; - right: 0; - height: 88px; - width: 66px; - border-radius: 8px; -} -.login-content__description-bar.bottomBar { - transform: rotate(30deg); - bottom: -4px; - left: -29px; - height: 50px; - width: 222px; -} -.login-content__description-bar.topBar { - transform: rotate(-330deg); - top: 10px; - right: -49px; - height: 50px; - width: 222px; -} -.login-footer { - margin-top: 30px; -} - -.login-footer__register { - margin-bottom: 10px; - font-weight: bold; -} - -.login-footer__language { - margin-top: 20px; -} - -.login-content__connectionStatus { - text-align: center; - margin: 20px 0; - padding: 20px; - font-weight: bold; -} diff --git a/webclient/src/containers/Login/Login.i18n.json b/webclient/src/containers/Login/Login.i18n.json deleted file mode 100644 index 13d248bb1..000000000 --- a/webclient/src/containers/Login/Login.i18n.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "A cross-platform virtual tabletop for multiplayer card games." - }, - "footer": { - "registerPrompt": "Not registered yet?", - "registerAction": "Create an account", - "credit": "Cockatrice is an open source project", - "version": "Version" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - } -} diff --git a/webclient/src/containers/Login/Login.tsx b/webclient/src/containers/Login/Login.tsx deleted file mode 100644 index fe008375c..000000000 --- a/webclient/src/containers/Login/Login.tsx +++ /dev/null @@ -1,360 +0,0 @@ -import { useState, useCallback } from 'react'; -import { styled } from '@mui/material/styles'; -import { useTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; -import { Navigate } from 'react-router-dom'; -import Button from '@mui/material/Button'; -import Paper from '@mui/material/Paper'; -import Typography from '@mui/material/Typography'; - -import { AuthenticationService } from 'api'; -import { RegistrationDialog, RequestPasswordResetDialog, ResetPasswordDialog, AccountActivationDialog } from 'dialogs'; -import { LanguageDropdown } from 'components'; -import { LoginForm } from 'forms'; -import { useReduxEffect, useFireOnce } from 'hooks'; -import { Images } from 'images'; -import { HostDTO, serverProps } from 'services'; -import { RouteEnum, WebSocketConnectOptions, getHostPort } from 'types'; -import { ServerSelectors, ServerTypes } from 'store'; -import Layout from 'containers/Layout/Layout'; - -import './Login.css'; -import { useToast } from 'components/Toast'; - -const PREFIX = 'Login'; - -const classes = { - root: `${PREFIX}-root` -}; - -const Root = styled('div')(({ theme }) => ({ - [`&.${classes.root}`]: { - '& .login-content__header': { - color: theme.palette.success.light - }, - - '& .login-content__description': { - backgroundColor: theme.palette.primary.main, - color: theme.palette.primary.contrastText, - }, - - '& .login-content__description-bar': { - backgroundColor: theme.palette.primary.dark, - }, - - '& .login-content__description-cards__card': { - backgroundColor: theme.palette.background.paper, - color: theme.palette.primary.main, - }, - - [theme.breakpoints.up('lg')]: { - '& .login-content': { - maxWidth: '1000px', - }, - - '& .login-content__form': { - width: '50%', - }, - - '& .login-content__description': { - width: '50%', - display: 'flex', - }, - }, - } -})); - -const Login = ({ state, description, connectOptions }: LoginProps) => { - const { t } = useTranslation(); - - const isConnected = AuthenticationService.isConnected(state); - - const [rememberLogin, setRememberLogin] = useState(null); - const [dialogState, setDialogState] = useState({ - passwordResetRequestDialog: false, - resetPasswordDialog: false, - registrationDialog: false, - activationDialog: false, - }); - const [userToResetPassword, setUserToResetPassword] = useState(null); - - const passwordResetToast = useToast({ key: 'password-reset-success', children: t('LoginContainer.toasts.passwordResetSuccess') }); - const accountActivatedToast = useToast({ - key: 'account-activation-success', - children: t('LoginContainer.toasts.accountActivationSuccess') - }); - - useReduxEffect(() => { - closeRequestPasswordResetDialog(); - openResetPasswordDialog(); - }, ServerTypes.RESET_PASSWORD_REQUESTED, []); - - useReduxEffect(() => { - passwordResetToast.openToast() - closeResetPasswordDialog(); - }, ServerTypes.RESET_PASSWORD_SUCCESS, []); - - useReduxEffect(() => { - accountActivatedToast.openToast() - closeActivateAccountDialog(); - }, ServerTypes.ACCOUNT_ACTIVATION_SUCCESS, []); - - useReduxEffect(() => { - closeRegistrationDialog(); - openActivateAccountDialog(); - }, ServerTypes.ACCOUNT_AWAITING_ACTIVATION, []); - - useReduxEffect(() => { - resetSubmitButton(); - }, [ServerTypes.CONNECTION_FAILED, ServerTypes.LOGIN_FAILED], []); - - useReduxEffect(({ options: { hashedPassword } }) => { - updateHost(hashedPassword, rememberLogin); - }, ServerTypes.LOGIN_SUCCESSFUL, [rememberLogin]); - - const showDescription = () => { - return !isConnected && description?.length; - }; - - const onSubmitLogin = useCallback((loginForm) => { - setRememberLogin(loginForm); - const { userName, password, selectedHost, remember } = loginForm; - - const options: WebSocketConnectOptions = { - ...getHostPort(selectedHost), - userName, - password - }; - - if (remember && !password) { - options.hashedPassword = selectedHost.hashedPassword; - } - - AuthenticationService.login(options as WebSocketConnectOptions); - }, []); - - const [submitButtonDisabled, resetSubmitButton, handleLogin] = useFireOnce(onSubmitLogin); - - const updateHost = (hashedPassword, { selectedHost, remember, userName }) => { - HostDTO.get(selectedHost.id).then(hostDTO => { - hostDTO.remember = remember; - hostDTO.userName = remember ? userName : null; - hostDTO.hashedPassword = remember ? hashedPassword : null; - - hostDTO.save(); - }); - }; - - const handleRegistrationDialogSubmit = (registerForm) => { - setRememberLogin(registerForm); - const { userName, password, email, country, realName, selectedHost } = registerForm; - - AuthenticationService.register({ - ...getHostPort(selectedHost), - userName, - password, - email, - country, - realName, - }); - }; - - const handleAccountActivationDialogSubmit = ({ token }) => { - AuthenticationService.activateAccount({ - ...connectOptions, - token, - }); - }; - - const handleRequestPasswordResetDialogSubmit = (form) => { - const { userName, email, selectedHost } = form; - const { host, port } = getHostPort(selectedHost); - - if (email) { - AuthenticationService.resetPasswordChallenge({ userName, email, host, port }); - } else { - setUserToResetPassword(userName); - AuthenticationService.resetPasswordRequest({ userName, host, port }); - } - }; - - const handleResetPasswordDialogSubmit = ({ userName, token, newPassword, selectedHost }) => { - const { host, port } = getHostPort(selectedHost); - - AuthenticationService.resetPassword({ userName, token, newPassword, host, port }); - }; - - const skipTokenRequest = (userName) => { - setUserToResetPassword(userName); - - setDialogState(s => ({ ...s, - passwordResetRequestDialog: false, - resetPasswordDialog: true, - })); - }; - - const closeRequestPasswordResetDialog = () => { - setDialogState(s => ({ ...s, passwordResetRequestDialog: false })); - } - - const openRequestPasswordResetDialog = () => { - setDialogState(s => ({ ...s, passwordResetRequestDialog: true })); - } - - const closeResetPasswordDialog = () => { - setDialogState(s => ({ ...s, resetPasswordDialog: false })); - } - - const openResetPasswordDialog = () => { - setDialogState(s => ({ ...s, resetPasswordDialog: true })); - } - - const closeRegistrationDialog = () => { - setDialogState(s => ({ ...s, registrationDialog: false })); - } - - const openRegistrationDialog = () => { - setDialogState(s => ({ ...s, registrationDialog: true })); - } - - const closeActivateAccountDialog = () => { - setDialogState(s => ({ ...s, activationDialog: false })); - }; - - const openActivateAccountDialog = () => { - setDialogState(s => ({ ...s, activationDialog: true })); - }; - - return ( - - - { isConnected && } - -
- -
-
- logo - COCKATRICE -
- { t('LoginContainer.header.title') } - { t('LoginContainer.header.subtitle') } -
- -
- - { - showDescription() && ( - - {description} - - ) - } - -
-
- { t('LoginContainer.footer.registerPrompt') } - -
- - { t('LoginContainer.footer.credit') } - { new Date().getUTCFullYear() } - - - { - serverProps.REACT_APP_VERSION && ( - - { t('LoginContainer.footer.version') }: { serverProps.REACT_APP_VERSION } - - ) - } - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Stock Player - 1mrlee -
-
-
-
- Stock Player - CyberX -
-
-
-
- Stock Player - Gamer69 -
-
-
- { /**/} -

{ t('LoginContainer.content.subtitle1') }

-

{ t('LoginContainer.content.subtitle2') }

-
-
- -
- - - - - - - - - - - ); -} - -interface LoginProps { - state: number; - description: string; - connectOptions: WebSocketConnectOptions; -} - -const mapStateToProps = state => ({ - state: ServerSelectors.getState(state), - description: ServerSelectors.getDescription(state), - connectOptions: ServerSelectors.getConnectOptions(state), -}); - -export default connect(mapStateToProps)(Login); diff --git a/webclient/src/containers/Logs/LogResults.css b/webclient/src/containers/Logs/LogResults.css deleted file mode 100644 index 3a6cbd185..000000000 --- a/webclient/src/containers/Logs/LogResults.css +++ /dev/null @@ -1,3 +0,0 @@ -.log-results { - margin-bottom: 20px; -} \ No newline at end of file diff --git a/webclient/src/containers/Logs/LogResults.tsx b/webclient/src/containers/Logs/LogResults.tsx deleted file mode 100644 index 06abdbce6..000000000 --- a/webclient/src/containers/Logs/LogResults.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import * as _ from 'lodash'; - -import AppBar from '@mui/material/AppBar'; -import Box from '@mui/material/Box'; -import Paper from '@mui/material/Paper'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Tab from '@mui/material/Tab'; -import Tabs from '@mui/material/Tabs'; -import Typography from '@mui/material/Typography'; - -import './LogResults.css'; - -const LogResults = (props) => { - const { logs } = props; - - const hasRoomLogs = logs.room && logs.room.length; - const hasGameLogs = logs.game && logs.game.length; - const hasChatLogs = logs.chat && logs.chat.length; - - const [value, setValue] = React.useState(0); - - const handleChange = (event, newValue) => { - setValue(newValue); - }; - - const headerCells = [ - { - label: 'Time' - }, - { - label: 'Sender Name' - }, - { - label: 'Sender IP' - }, - { - label: 'Message' - }, - { - label: 'Target ID' - }, - { - label: 'Target Name' - } - ]; - - return ( -
- - - - - - - - - - - - - - - - -
- ) -}; - -const a11yProps = index => { - return { - id: `simple-tab-${index}`, - 'aria-controls': `simple-tabpanel-${index}`, - }; -}; - -const TabPanel = ({ children, value, index, ...other }) => { - return ( - - ); -}; - -const Results = ({ headerCells, logs }) => ( - - - - - { _.map(headerCells, ({ label }) => ( - {label} - ))} - - - - { _.map(logs, ({ time, senderName, senderIp, message, targetId, targetName }, index) => ( - - {time} - {senderName} - {senderIp} - {message} - {targetId} - {targetName} - - ))} - -
-
-); - -export default LogResults; diff --git a/webclient/src/containers/Logs/Logs.css b/webclient/src/containers/Logs/Logs.css deleted file mode 100644 index 37ac8eb03..000000000 --- a/webclient/src/containers/Logs/Logs.css +++ /dev/null @@ -1,14 +0,0 @@ -.moderator-logs { - height: 100%; - display: flex; - padding: 20px; -} - -.moderator-logs__form { - width: 40%; - margin-right: 20px; -} - -.moderator-logs__results { - width: 100%; -} \ No newline at end of file diff --git a/webclient/src/containers/Logs/Logs.tsx b/webclient/src/containers/Logs/Logs.tsx deleted file mode 100644 index a34a7b008..000000000 --- a/webclient/src/containers/Logs/Logs.tsx +++ /dev/null @@ -1,109 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import * as _ from 'lodash'; - -import { ModeratorService } from 'api'; -import { AuthGuard, ModGuard } from 'components'; -import { SearchForm } from 'forms'; -import { ServerDispatch, ServerSelectors, ServerStateLogs } from 'store'; -import { LogFilters } from 'types'; - -import LogResults from './LogResults'; -import './Logs.css'; - -class Logs extends Component { - MAXIMUM_RESULTS = 1000; - - constructor(props) { - super(props); - - this.onSubmit = this.onSubmit.bind(this); - } - - componentWillUnmount() { - ServerDispatch.clearLogs(); - } - - onSubmit(fields: LogFilters) { - const trimmedFields: any = this.trimFields(fields); - - const { userName, ipAddress, gameName, gameId, message, logLocation } = trimmedFields; - - const required = _.filter({ - userName, ipAddress, gameName, gameId, message - }, field => field); - - if (logLocation) { - trimmedFields.logLocation = this.flattenLogLocations(logLocation); - } - - trimmedFields.maximumResults = this.MAXIMUM_RESULTS; - - if (_.size(required)) { - ModeratorService.viewLogHistory(trimmedFields); - } else { - // @TODO use yet-to-be-implemented banner/alert - } - } - - private trimFields(fields) { - return _.reduce(fields, (obj, field, key) => { - if (typeof field === 'string') { - const trimmed = _.trim(field); - - if (!!trimmed) { - obj[key] = trimmed; - } - } else { - obj[key] = field; - } - - return obj; - }, {}); - } - - private flattenLogLocations(logLocations) { - return _.reduce(logLocations, (arr, loc, key) => { - arr.push(key); - return arr; - }, []) - } - - render() { - return ( -
- - - -
- -
- -
- -
-
- ) - } -} - -interface LogsTypes { - logs: ServerStateLogs -} - -const mapStateToProps = state => ({ - logs: ServerSelectors.getLogs(state) -}); - -export default connect(mapStateToProps)(Logs); - - - - - - - - - - diff --git a/webclient/src/containers/Player/Player.tsx b/webclient/src/containers/Player/Player.tsx deleted file mode 100644 index 74feff8bd..000000000 --- a/webclient/src/containers/Player/Player.tsx +++ /dev/null @@ -1,18 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import Layout from 'containers/Layout/Layout'; - -import { AuthGuard } from 'components'; - -class Player extends Component { - render() { - return ( - - - "Player" - - ) - } -} - -export default Player; diff --git a/webclient/src/containers/Room/Games.css b/webclient/src/containers/Room/Games.css deleted file mode 100644 index 623ab47f5..000000000 --- a/webclient/src/containers/Room/Games.css +++ /dev/null @@ -1,30 +0,0 @@ -.games { -} - -.games-header, -.game { - display: flex; - padding: 10px; - border-bottom: 1px solid black; -} - -.games-header__cell { - max-width: 200px; -} - -.games-header__label, -.game__detail { - width: 10%; - flex-grow: 0; -} - -.games-header__label.description, -.game__detail.description { - width: 20%; - flex-grow: 1; -} - -.games-header__label.creator, -.game__detail.creator { - width: 20%; -} diff --git a/webclient/src/containers/Room/Games.tsx b/webclient/src/containers/Room/Games.tsx deleted file mode 100644 index 67a9239d3..000000000 --- a/webclient/src/containers/Room/Games.tsx +++ /dev/null @@ -1,143 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import * as _ from 'lodash'; - -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import TableSortLabel from '@mui/material/TableSortLabel'; -import Tooltip from '@mui/material/Tooltip'; - -// import { RoomsService } from "AppShell/common/services"; - -import { SortUtil, RoomsDispatch, RoomsSelectors } from 'store'; -import { UserDisplay } from 'components'; - -import './Games.css'; - -// @TODO run interval to update timeSinceCreated -class Games extends Component { - private headerCells = [ - { - label: 'Age', - field: 'startTime' - }, - { - label: 'Description', - field: 'description' - }, - { - label: 'Creator', - field: 'creatorInfo.name' - }, - { - label: 'Type', - field: 'gameType' - }, - { - label: 'Restrictions', - // field: "?" - }, - { - label: 'Players', - // field: ["maxPlayers", "playerCount"] - }, - { - label: 'Spectators', - field: 'spectatorsCount' - }, - ]; - - handleSort(sortByField) { - const { room: { roomId }, sortBy } = this.props; - const { field, order } = SortUtil.toggleSortBy(sortByField, sortBy); - RoomsDispatch.sortGames(roomId, field, order); - } - - private isUnavailableGame({ started, maxPlayers, playerCount }) { - return !started && playerCount < maxPlayers; - } - - private isPasswordProtectedGame({ withPassword }) { - return !withPassword; - } - - private isBuddiesOnlyGame({ onlyBuddies }) { - return !onlyBuddies; - } - - render() { - const { room, sortBy } = this.props; - - const games = room.gameList.filter(game => ( - this.isUnavailableGame(game) && - this.isPasswordProtectedGame(game) && - this.isBuddiesOnlyGame(game) - )); - - return ( -
- - - - { _.map(this.headerCells, ({ label, field }) => { - const active = field === sortBy.field; - const order = sortBy.order.toLowerCase(); - const sortDirection = active ? order : false; - - return ( - - {!field ? label : ( - this.handleSort(field)} - > - {label} - - )} - - ); - })} - - - - { _.map(games, ({ description, gameId, gameType, creatorInfo, maxPlayers, playerCount, spectatorsCount, startTime }) => ( - - {startTime} - - -
- {description} -
-
-
- - - - {gameType} - ? - {`${playerCount}/${maxPlayers}`} - {spectatorsCount} -
- ))} -
-
-
- ); - } -} - -interface GamesProps { - room: any; - sortBy: any; -} - -const mapStateToProps = state => ({ - sortBy: RoomsSelectors.getSortGamesBy(state) -}); - -export default connect(mapStateToProps)(Games); diff --git a/webclient/src/containers/Room/Messages.css b/webclient/src/containers/Room/Messages.css deleted file mode 100644 index 731002cb8..000000000 --- a/webclient/src/containers/Room/Messages.css +++ /dev/null @@ -1,17 +0,0 @@ -.messages { - height: 100%; - width: 100%; - padding: 10px; - font-size: 12px; - line-height: 1.3; -} - -.message-wrapper { - padding: 5px 0; - margin: 2px 0; - border-bottom: 1px dashed rgba(0, 0, 0, 0.25); -} - -.message-wrapper:last-of-type { - border: 0; -} diff --git a/webclient/src/containers/Room/Messages.tsx b/webclient/src/containers/Room/Messages.tsx deleted file mode 100644 index 24b576759..000000000 --- a/webclient/src/containers/Room/Messages.tsx +++ /dev/null @@ -1,20 +0,0 @@ -// eslint-disable-next-line -import React from "react"; - -import { Message } from 'components'; - -import './Messages.css'; - -const Messages = ({ messages }) => ( -
- { - messages && messages.map((message, index) => ( -
- -
- )) - } -
-); - -export default Messages; diff --git a/webclient/src/containers/Room/OpenGames.css b/webclient/src/containers/Room/OpenGames.css deleted file mode 100644 index 623ab47f5..000000000 --- a/webclient/src/containers/Room/OpenGames.css +++ /dev/null @@ -1,30 +0,0 @@ -.games { -} - -.games-header, -.game { - display: flex; - padding: 10px; - border-bottom: 1px solid black; -} - -.games-header__cell { - max-width: 200px; -} - -.games-header__label, -.game__detail { - width: 10%; - flex-grow: 0; -} - -.games-header__label.description, -.game__detail.description { - width: 20%; - flex-grow: 1; -} - -.games-header__label.creator, -.game__detail.creator { - width: 20%; -} diff --git a/webclient/src/containers/Room/OpenGames.tsx b/webclient/src/containers/Room/OpenGames.tsx deleted file mode 100644 index 49d4d2503..000000000 --- a/webclient/src/containers/Room/OpenGames.tsx +++ /dev/null @@ -1,143 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import * as _ from 'lodash'; - -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import TableSortLabel from '@mui/material/TableSortLabel'; -import Tooltip from '@mui/material/Tooltip'; - -// import { RoomsService } from "AppShell/common/services"; - -import { SortUtil, RoomsDispatch, RoomsSelectors } from 'store'; -import { UserDisplay } from 'components'; - -import './OpenGames.css'; - -// @TODO run interval to update timeSinceCreated -class OpenGames extends Component { - private headerCells = [ - { - label: 'Age', - field: 'startTime' - }, - { - label: 'Description', - field: 'description' - }, - { - label: 'Creator', - field: 'creatorInfo.name' - }, - { - label: 'Type', - field: 'gameType' - }, - { - label: 'Restrictions', - // field: "?" - }, - { - label: 'Players', - // field: ["maxPlayers", "playerCount"] - }, - { - label: 'Spectators', - field: 'spectatorsCount' - }, - ]; - - handleSort(sortByField) { - const { room: { roomId }, sortBy } = this.props; - const { field, order } = SortUtil.toggleSortBy(sortByField, sortBy); - RoomsDispatch.sortGames(roomId, field, order); - } - - private isUnavailableGame({ started, maxPlayers, playerCount }) { - return !started && playerCount < maxPlayers; - } - - private isPasswordProtectedGame({ withPassword }) { - return !withPassword; - } - - private isBuddiesOnlyGame({ onlyBuddies }) { - return !onlyBuddies; - } - - render() { - const { room, sortBy } = this.props; - - const games = room.gameList.filter(game => ( - this.isUnavailableGame(game) && - this.isPasswordProtectedGame(game) && - this.isBuddiesOnlyGame(game) - )); - - return ( -
- - - - { _.map(this.headerCells, ({ label, field }) => { - const active = field === sortBy.field; - const order = sortBy.order.toLowerCase(); - const sortDirection = active ? order : false; - - return ( - - {!field ? label : ( - this.handleSort(field)} - > - {label} - - )} - - ); - })} - - - - { _.map(games, ({ description, gameId, gameType, creatorInfo, maxPlayers, playerCount, spectatorsCount, startTime }) => ( - - {startTime} - - -
- {description} -
-
-
- - - - {gameType} - ? - {`${playerCount}/${maxPlayers}`} - {spectatorsCount} -
- ))} -
-
-
- ); - } -} - -interface OpenGamesProps { - room: any; - sortBy: any; -} - -const mapStateToProps = state => ({ - sortBy: RoomsSelectors.getSortGamesBy(state) -}); - -export default connect(mapStateToProps)(OpenGames); diff --git a/webclient/src/containers/Room/Room.css b/webclient/src/containers/Room/Room.css deleted file mode 100644 index 4b6aaec93..000000000 --- a/webclient/src/containers/Room/Room.css +++ /dev/null @@ -1,45 +0,0 @@ -.room-view, -.room-view__main, -.room-view__games, -.room-view__messages, -.room-view__messages-content, -.room-view__side { - height: 100%; -} - -.room-view, -.room-view__messages, -.room-view__side { - display: flex; - flex-direction: column; -} - -.room-view__main { - overflow: hidden; -} - -.room-view__messages-sayMessage { - width: 100%; - margin: 10px auto 2px; -} - -.room-view__side-label { - position: sticky; - top: 0; - padding: 10px; - background: white; - z-index: 1; -} - -.room-view__side-list, -.room-view__side-list .room-view__side-list__item { - height: 100%; -} - -.room-view__side-list .room-view__side-list__item { - padding: 0; -} - -.room-view__side-list .room-view__side-list__item .user-display__details { - padding: 0 10px; -} \ No newline at end of file diff --git a/webclient/src/containers/Room/Room.tsx b/webclient/src/containers/Room/Room.tsx deleted file mode 100644 index c73930d7f..000000000 --- a/webclient/src/containers/Room/Room.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { useNavigate, useParams, generatePath } from 'react-router-dom'; - -import ListItem from '@mui/material/ListItem'; -import Paper from '@mui/material/Paper'; - -import { RoomsService } from 'api'; -import { ScrollToBottomOnChanges, ThreePaneLayout, UserDisplay, VirtualList, AuthGuard } from 'components'; -import { RoomsStateMessages, RoomsStateRooms, JoinedRooms, RoomsSelectors, RoomsTypes } from 'store'; -import { RouteEnum } from 'types'; -import Layout from 'containers/Layout/Layout'; - -import OpenGames from './OpenGames'; -import Messages from './Messages'; -import SayMessage from './SayMessage'; - -import './Room.css'; - -// @TODO (3) -const Room = (props) => { - const { joined, rooms, messages } = props; - const navigate = useNavigate(); - const params = useParams(); - - const roomId = parseInt(params.roomId, 0); - const room = rooms[roomId]; - const roomMessages = messages[roomId]; - const users = room.userList; - - useEffect(() => { - if (!joined.find(({ roomId: id }) => id === roomId)) { - navigate(generatePath(RouteEnum.SERVER)); - } - }, [joined]); - - const handleRoomSay = ({ message }) => { - if (message) { - RoomsService.roomSay(roomId, message); - } - } - - return ( - - - -
- - - - )} - - bottom={( -
- - - )} /> - - - - -
- )} - - side={( - -
- Users in this room: {users.length} -
- users[index].name } - items={ users.map(user => ( - - - - )) } - /> -
- )} - /> -
-
- ); -} - -interface RoomProps { - messages: RoomsStateMessages; - rooms: RoomsStateRooms; - joined: JoinedRooms; -} - -const mapStateToProps = state => ({ - messages: RoomsSelectors.getMessages(state), - rooms: RoomsSelectors.getRooms(state), - joined: RoomsSelectors.getJoinedRooms(state), -}); - -export default connect(mapStateToProps)(Room); diff --git a/webclient/src/containers/Room/SayMessage.tsx b/webclient/src/containers/Room/SayMessage.tsx deleted file mode 100644 index 4dd62dfb4..000000000 --- a/webclient/src/containers/Room/SayMessage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { Form } from 'react-final-form' - -import { InputAction } from 'components'; - -const SayMessage = ({ onSubmit }) => ( -
- {({ handleSubmit, form }) => ( - { - handleSubmit(e) - form.restart() - }}> - - - )} - -); - -export default SayMessage; diff --git a/webclient/src/containers/Server/Rooms.css b/webclient/src/containers/Server/Rooms.css deleted file mode 100644 index bfcdc82cf..000000000 --- a/webclient/src/containers/Server/Rooms.css +++ /dev/null @@ -1,26 +0,0 @@ -.rooms { -} - -.rooms-header, -.room { - display: flex; - padding: 10px; - border-bottom: 1px solid black; -} - -.rooms-header__label, -.room__detail { - width: 10%; - flex-grow: 0; -} - -.rooms-header__label.name, -.room__detail.name { - width: 20%; -} - -.rooms-header__label.description, -.room__detail.description { - width: 30%; - flex-grow: 1; -} \ No newline at end of file diff --git a/webclient/src/containers/Server/Rooms.tsx b/webclient/src/containers/Server/Rooms.tsx deleted file mode 100644 index 517f24380..000000000 --- a/webclient/src/containers/Server/Rooms.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// eslint-disable-next-line -import React from "react"; -import { generatePath, useNavigate } from 'react-router-dom'; -import * as _ from 'lodash'; - -import Button from '@mui/material/Button'; -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; - - -import { RoomsService } from 'api'; -import { RouteEnum } from 'types'; - -import './Rooms.css'; - -const Rooms = ({ rooms, joinedRooms }) => { - const navigate = useNavigate(); - - function onClick(roomId) { - if (_.find(joinedRooms, room => room.roomId === roomId)) { - navigate(generatePath(RouteEnum.ROOM, { roomId })); - } else { - RoomsService.joinRoom(roomId); - } - } - - return ( -
- - - - Name - Description - Permissions - Players - Games - - - - - { _.map(rooms, ({ description, gameCount, name, permissionlevel, playerCount, roomId }) => ( - - {name} - {description} - {permissionlevel} - {playerCount} - {gameCount} - - - - - ))} - -
-
- ); -}; - -export default Rooms; diff --git a/webclient/src/containers/Server/Server.css b/webclient/src/containers/Server/Server.css deleted file mode 100644 index 8d4e2e8dd..000000000 --- a/webclient/src/containers/Server/Server.css +++ /dev/null @@ -1,34 +0,0 @@ -.server, -.server-rooms, -.server-rooms__side { - height: 100%; -} - -.server { - display: flex; - flex-direction: column; - align-items: center; -} - - -.serverRoomWrapper { - height: 100%; -} - -.serverMessage { - height: 100%; - padding: 20px; - margin-bottom: 2px; -} - -.server-rooms { - width: 100%; -} - -.server-rooms__side-label { - position: sticky; - top: 0; - padding: 10px; - background: white; - z-index: 1; -} diff --git a/webclient/src/containers/Server/Server.tsx b/webclient/src/containers/Server/Server.tsx deleted file mode 100644 index 7ed5e2464..000000000 --- a/webclient/src/containers/Server/Server.tsx +++ /dev/null @@ -1,77 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import { generatePath, useNavigate } from 'react-router-dom'; - -import ListItem from '@mui/material/ListItem'; -import Paper from '@mui/material/Paper'; - -import { AuthGuard, ThreePaneLayout, UserDisplay, VirtualList } from 'components'; -import { useReduxEffect } from 'hooks'; -import { RoomsSelectors, RoomsTypes, ServerSelectors } from 'store'; -import { Room, RouteEnum, User } from 'types'; -import Rooms from './Rooms'; -import Layout from 'containers/Layout/Layout'; - -import './Server.css'; - -const Server = ({ message, rooms, joinedRooms, users }: ServerProps) => { - const navigate = useNavigate(); - - useReduxEffect((action: any) => { - const roomId = action.roomInfo.roomId.toString(); - navigate(generatePath(RouteEnum.ROOM, { roomId })); - }, RoomsTypes.JOIN_ROOM, []); - - return ( - - - - - - - )} - - bottom={( - -
- - )} - - side={( - -
- Users connected to server: {users.length} -
- users[index].name } - items={ users.map(user => ( - - - - )) } - /> -
- )} - /> - - ); -} - -interface ServerProps { - message: string; - rooms: Room[]; - joinedRooms: Room[]; - users: User[]; -} - -const mapStateToProps = state => ({ - message: ServerSelectors.getMessage(state), - rooms: RoomsSelectors.getRooms(state), - joinedRooms: RoomsSelectors.getJoinedRooms(state), - users: ServerSelectors.getUsers(state) -}); - -export default connect(mapStateToProps)(Server); diff --git a/webclient/src/containers/Unsupported/Unsupported.css b/webclient/src/containers/Unsupported/Unsupported.css deleted file mode 100644 index 46752e746..000000000 --- a/webclient/src/containers/Unsupported/Unsupported.css +++ /dev/null @@ -1,18 +0,0 @@ -.Unsupported { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - padding: 20px; -} - -.Unsupported-paper { - width: 600px; - max-width: 100%; - padding: 40px; - text-align: center; -} - -.Unsupported-paper__header { - margin-bottom: 40px; -} diff --git a/webclient/src/containers/Unsupported/Unsupported.i18n.json b/webclient/src/containers/Unsupported/Unsupported.i18n.json deleted file mode 100644 index 225a13a67..000000000 --- a/webclient/src/containers/Unsupported/Unsupported.i18n.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - } -} diff --git a/webclient/src/containers/Unsupported/Unsupported.tsx b/webclient/src/containers/Unsupported/Unsupported.tsx deleted file mode 100644 index b666da12e..000000000 --- a/webclient/src/containers/Unsupported/Unsupported.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { connect } from 'react-redux'; -import { useTranslation } from 'react-i18next'; -import Paper from '@mui/material/Paper'; -import Typography from '@mui/material/Typography'; -import Layout from 'containers/Layout/Layout'; - -import './Unsupported.css'; - -const Unsupported = () => { - const { t } = useTranslation(); - - return ( - - -
- { t('UnsupportedContainer.title') } - { t('UnsupportedContainer.subtitle1') } -
- - { t('UnsupportedContainer.subtitle2') } -
-
- ); -}; - -const mapStateToProps = state => ({ - -}); - -export default connect(mapStateToProps)(Unsupported); diff --git a/webclient/src/containers/index.ts b/webclient/src/containers/index.ts deleted file mode 100644 index f43de1c8e..000000000 --- a/webclient/src/containers/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { default as AppShell } from './App/AppShell'; -export { default as Account } from './Account/Account'; -export { default as Game } from './Game/Game'; -export { default as Decks } from './Decks/Decks'; -export { default as Room } from './Room/Room'; -export { default as Player } from './Player/Player'; -export { default as Server } from './Server/Server'; -export { default as Logs } from './Logs/Logs'; -export { default as Login } from './Login/Login'; -export { default as Initialize } from './Initialize/Initialize'; -export { default as Unsupported } from './Unsupported/Unsupported'; diff --git a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css deleted file mode 100644 index 5175ab845..000000000 --- a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css +++ /dev/null @@ -1,13 +0,0 @@ -.dialog-title { - display: flex; - justify-content: space-between; - align-items: center; -} - -.MuiDialogTitle-root.dialog-title { - padding-bottom: 0; -} - -.content { - margin-bottom: 20px; -} \ No newline at end of file diff --git a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.i18n.json b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.i18n.json deleted file mode 100644 index 9cfd876f5..000000000 --- a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.i18n.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - } -} diff --git a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx deleted file mode 100644 index cb5239619..000000000 --- a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { AccountActivationForm } from 'forms'; - -import './AccountActivationDialog.css'; - -const AccountActivationDialog = ({ classes, handleClose, isOpen, onSubmit }: any) => { - const { t } = useTranslation(); - - const handleOnClose = () => { - handleClose(); - } - - return ( - - - { t('AccountActivationDialog.title') } - - {handleOnClose ? ( - - - - ) : null} - - -
- { t('AccountActivationDialog.subtitle1') } - { t('AccountActivationDialog.subtitle2') } -
- - -
-
- ); -}; - -export default AccountActivationDialog; diff --git a/webclient/src/dialogs/CardImportDialog/CardImportDialog.css b/webclient/src/dialogs/CardImportDialog/CardImportDialog.css deleted file mode 100644 index b089ed200..000000000 --- a/webclient/src/dialogs/CardImportDialog/CardImportDialog.css +++ /dev/null @@ -1,5 +0,0 @@ -.dialog-title { - display: flex; - justify-content: space-between; - align-items: center; -} diff --git a/webclient/src/dialogs/CardImportDialog/CardImportDialog.tsx b/webclient/src/dialogs/CardImportDialog/CardImportDialog.tsx deleted file mode 100644 index 8011d317a..000000000 --- a/webclient/src/dialogs/CardImportDialog/CardImportDialog.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; - -import { CardImportForm } from 'forms'; - -import './CardImportDialog.css'; - -const CardImportDialog = ({ classes, handleClose, isOpen }: any) => { - const handleOnClose = () => { - handleClose(); - } - - return ( - - - Import Cards - - {handleOnClose ? ( - - - - ) : null} - - - - - - ); -}; - -export default CardImportDialog; diff --git a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.css b/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.css deleted file mode 100644 index e8a350f0d..000000000 --- a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.css +++ /dev/null @@ -1,26 +0,0 @@ -.KnownHostDialog { - -} - -.KnownHostDialog .MuiDialog-paper { - width: 100%; - max-width: 420px; -} - -.dialog-title__wrapper { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - border-bottom: 1px solid; - padding-bottom: 10px; -} - -.dialog-title__label { - display: flex; - align-items: center; -} - -.dialog-content__subtitle.MuiTypography-root { - margin-bottom: 20px; -} diff --git a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.i18n.json b/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.i18n.json deleted file mode 100644 index 4ae521050..000000000 --- a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.i18n.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - } -} diff --git a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.tsx b/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.tsx deleted file mode 100644 index 5bde19d92..000000000 --- a/webclient/src/dialogs/KnownHostDialog/KnownHostDialog.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import AddIcon from '@mui/icons-material/Add'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { KnownHostForm } from 'forms'; - -import './KnownHostDialog.css'; - -const PREFIX = 'KnownHostDialog'; - -const classes = { - root: `${PREFIX}-root` -}; - -const StyledDialog = styled(Dialog)(({ theme }) => ({ - [`&.${classes.root}`]: { - '& .dialog-title__wrapper': { - borderColor: theme.palette.grey[300] - } - } -})); - -const KnownHostDialog = ({ handleClose, onRemove, onSubmit, isOpen, host }: any) => { - const { t } = useTranslation(); - - const mode = host ? 'edit' : 'add'; - - const handleOnClose = () => { - if (handleClose) { - handleClose(); - } - }; - - return ( - - -
- { t('KnownHostDialog.title', { mode }) } - - {handleClose ? ( - - - - ) : null} -
-
- - - { t('KnownHostDialog.subtitle') } - - - -
- ); -}; - -export default KnownHostDialog; diff --git a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.css b/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.css deleted file mode 100644 index 73822f190..000000000 --- a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.css +++ /dev/null @@ -1,10 +0,0 @@ -.dialog-title { - display: flex; - justify-content: space-between; - align-items: center; -} - -.dialog-content { - width: 700px; - max-width: 100%; -} diff --git a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.i18n.json b/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.i18n.json deleted file mode 100644 index bc0aa646d..000000000 --- a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.i18n.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "RegistrationDialog": { - "title": "Create New Account" - } -} diff --git a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.tsx b/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.tsx deleted file mode 100644 index 2388dddb3..000000000 --- a/webclient/src/dialogs/RegistrationDialog/RegistrationDialog.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { RegisterForm } from 'forms'; - -import './RegistrationDialog.css'; - -const RegistrationDialog = ({ classes, handleClose, isOpen, onSubmit }: any) => { - const { t } = useTranslation(); - - const handleOnClose = () => { - handleClose(); - } - - return ( - - - { t('RegistrationDialog.title') } - - {handleOnClose ? ( - - - - ) : null} - - - - - - ); -}; - -export default RegistrationDialog; diff --git a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.css b/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.css deleted file mode 100644 index 731927c13..000000000 --- a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.css +++ /dev/null @@ -1,5 +0,0 @@ -.dialog-title { - display: flex; - justify-content: space-between; - align-items: center; -} diff --git a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.i18n.json b/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.i18n.json deleted file mode 100644 index c287b7a82..000000000 --- a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.i18n.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - } -} diff --git a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.tsx b/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.tsx deleted file mode 100644 index be2032a08..000000000 --- a/webclient/src/dialogs/RequestPasswordResetDialog/RequestPasswordResetDialog.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { RequestPasswordResetForm } from 'forms'; - -import './RequestPasswordResetDialog.css'; - -const RequestPasswordResetDialog = ({ classes, handleClose, isOpen, onSubmit, skipTokenRequest }: any) => { - const { t } = useTranslation(); - - const handleOnClose = () => { - handleClose(); - } - - return ( - - - { t('RequestPasswordResetDialog.title') } - - {handleOnClose ? ( - - - - ) : null} - - - - - - ); -}; - -export default RequestPasswordResetDialog; diff --git a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.css b/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.css deleted file mode 100644 index 731927c13..000000000 --- a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.css +++ /dev/null @@ -1,5 +0,0 @@ -.dialog-title { - display: flex; - justify-content: space-between; - align-items: center; -} diff --git a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.i18n.json b/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.i18n.json deleted file mode 100644 index 8047dcae0..000000000 --- a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.i18n.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ResetPasswordDialog": { - "title": "Reset Password" - } -} diff --git a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.tsx b/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.tsx deleted file mode 100644 index 877c8729f..000000000 --- a/webclient/src/dialogs/ResetPasswordDialog/ResetPasswordDialog.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -import { ResetPasswordForm } from 'forms'; - -import './ResetPasswordDialog.css'; - -const ResetPasswordDialog = ({ classes, handleClose, isOpen, onSubmit, userName }: any) => { - const { t } = useTranslation(); - - const handleOnClose = () => { - handleClose(); - } - - return ( - - - {t('ResetPasswordDialog.title')} - - {handleOnClose ? ( - - - - ) : null} - - - - - - ); -}; - -export default ResetPasswordDialog; diff --git a/webclient/src/dialogs/index.ts b/webclient/src/dialogs/index.ts deleted file mode 100644 index 5c5ace1c5..000000000 --- a/webclient/src/dialogs/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { default as AccountActivationDialog } from './AccountActivationDialog/AccountActivationDialog'; -export { default as CardImportDialog } from './CardImportDialog/CardImportDialog'; -export { default as KnownHostDialog } from './KnownHostDialog/KnownHostDialog'; -export { default as RegistrationDialog } from './RegistrationDialog/RegistrationDialog'; -export { default as RequestPasswordResetDialog } from './RequestPasswordResetDialog/RequestPasswordResetDialog'; -export { default as ResetPasswordDialog } from './ResetPasswordDialog/ResetPasswordDialog'; diff --git a/webclient/src/forms/AccountActivationForm/AccountActivationForm.css b/webclient/src/forms/AccountActivationForm/AccountActivationForm.css deleted file mode 100644 index ffb4ecc77..000000000 --- a/webclient/src/forms/AccountActivationForm/AccountActivationForm.css +++ /dev/null @@ -1,12 +0,0 @@ -.AccountActivationForm { - width: 100%; - padding-bottom: 15px; -} - -.AccountActivationForm-item { - margin-bottom: 20px; -} - -.AccountActivationForm-submit { - width: 100%; -} diff --git a/webclient/src/forms/AccountActivationForm/AccountActivationForm.i18n.json b/webclient/src/forms/AccountActivationForm/AccountActivationForm.i18n.json deleted file mode 100644 index 0b510fb83..000000000 --- a/webclient/src/forms/AccountActivationForm/AccountActivationForm.i18n.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - } -} diff --git a/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx b/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx deleted file mode 100644 index 61b20f9a5..000000000 --- a/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// eslint-disable-next-line -import React, { useState } from "react"; -import { connect } from 'react-redux'; -import { Form, Field } from 'react-final-form'; -import { OnChange } from 'react-final-form-listeners'; -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; -import Typography from '@mui/material/Typography'; - -import { InputField, KnownHosts } from 'components'; -import { FormKey } from 'types'; - -import './AccountActivationForm.css'; -import { useReduxEffect } from 'hooks'; -import { ServerTypes } from 'store'; - -const AccountActivationForm = ({ onSubmit }) => { - const [errorMessage, setErrorMessage] = useState(false); - const { t } = useTranslation(); - - useReduxEffect(() => { - setErrorMessage(true); - }, ServerTypes.ACCOUNT_ACTIVATION_FAILED, []); - - const handleOnSubmit = ({ token, ...values }) => { - setErrorMessage(false); - - token = token?.trim(); - - onSubmit({ token, ...values }); - } - - const validate = values => { - const errors: any = {}; - - if (!values.token) { - errors.token = t('Common.validation.required'); - } - - return errors; - }; - - return ( -
- {({ handleSubmit, form }) => { - return ( - -
- -
- - {errorMessage && ( -
- { t('AccountActivationForm.error.failed') } -
- )} - - -
- ); - }} - - ); -}; - -export default AccountActivationForm; diff --git a/webclient/src/forms/CardImportForm/CardImportForm.css b/webclient/src/forms/CardImportForm/CardImportForm.css deleted file mode 100644 index 56ac3e16e..000000000 --- a/webclient/src/forms/CardImportForm/CardImportForm.css +++ /dev/null @@ -1,35 +0,0 @@ -.cardImportForm { - width: 550px; -} - -.cardImportForm-content.done { - font-size: 32px; - height: 150px; - display: flex; - align-items: center; - justify-content: center; -} - -.cardImportForm-actions { - display: flex; - justify-content: flex-end; - margin-top: 20px; -} - -.cardImportForm-error { - color: red; -} - -.card-import-list { - height: 300px; - line-height: 1; - border: 1px solid lightgrey; - padding: 10px; -} - -.loading { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} diff --git a/webclient/src/forms/CardImportForm/CardImportForm.tsx b/webclient/src/forms/CardImportForm/CardImportForm.tsx deleted file mode 100644 index b9745f31e..000000000 --- a/webclient/src/forms/CardImportForm/CardImportForm.tsx +++ /dev/null @@ -1,227 +0,0 @@ -// eslint-disable-next-line -import React, { useEffect, useState } from 'react'; -import { connect } from 'react-redux'; -import { Form, Field, reduxForm } from 'redux-form' - -import Button from '@mui/material/Button'; -import Stepper from '@mui/material/Stepper'; -import Step from '@mui/material/Step'; -import StepLabel from '@mui/material/StepLabel'; -import CircularProgress from '@mui/material/CircularProgress'; - -import { InputField, VirtualList } from 'components'; -import { cardImporterService, CardDTO, SetDTO, TokenDTO } from 'services'; -import { FormKey } from 'types'; - -import './CardImportForm.css'; - -const CardImportForm = (props) => { - const { handleSubmit, onSubmit: onClose } = props; - - const [loading, setLoading] = useState(false); - const [activeStep, setActiveStep] = useState(0); - const [importedCards, setImportedCards] = useState([]); - const [importedSets, setImportedSets] = useState([]); - const [error, setError] = useState(null); - - useEffect(() => { - if (loading) { - setError(null); - } - }, [loading]) - - const steps = ['Imports sets', 'Save sets', 'Import tokens', 'Finished']; - - const handleNext = () => { - setActiveStep((prevActiveStep) => prevActiveStep + 1); - }; - - const handleBack = () => { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - }; - - const handleCardDownload = ({ cardDownloadUrl }) => { - setLoading(true); - - cardImporterService.importCards(cardDownloadUrl) - .then(({ cards, sets }) => { - setImportedCards(cards); - setImportedSets(sets); - - handleNext(); - }) - .catch(({ message }) => setError(message)) - .finally(() => setLoading(false)); - }; - - const handleCardSave = async () => { - setLoading(true); - - try { - await CardDTO.bulkAdd(importedCards); - await SetDTO.bulkAdd(importedSets); - - handleNext(); - } catch (e) { - console.error(e); - setError('Failed to save cards'); - } - - setLoading(false); - }; - - const handleTokenDownload = ({ tokenDownloadUrl }) => { - setLoading(true); - - cardImporterService.importTokens(tokenDownloadUrl) - .then(async tokens => { - await TokenDTO.bulkAdd(tokens); - handleNext(); - }) - .catch(({ message }) => setError(message)) - .finally(() => setLoading(false)); - }; - - const getStepContent = (stepIndex) => { - switch (stepIndex) { - case 0: return ( -
-
- -
- -
- -
- -
- -
-
- ); - - case 1: return ( -
-
- -
- -
- - -
- -
- -
-
- ); - - case 2: return ( -
-
- -
- -
- - -
- -
- -
-
- ); - - case 3: return ( -
-
Finished!
- -
- - -
-
- ); - } - }; - - return ( -
- - {steps.map((label) => ( - - {label} - - ))} - - -
- { getStepContent(activeStep) } -
- - { loading && ( -
- -
- ) } -
- ); -}; - -const BackButton = ({ click, disabled }) => ( - -); - -const ErrorMessage = ({ error }) => { - return error && ( -
{error}
- ); -}; - -const CardsImported = ({ cards, sets }) => { - const items = [ - ( -
- Import finished: {cards.length} cards. -
- ), - - (
), - - ...sets.map(set => ( -
{set.name}: {set.cards.length} cards imported
- )) - ]; - - return ( -
- index } - items={items} - size={15} - /> -
- ); -}; - -const propsMap = { - form: FormKey.CARD_IMPORT, - onClose: Function -}; - -const mapStateToProps = () => ({ - initialValues: { - cardDownloadUrl: 'https://www.mtgjson.com/api/v5/AllPrintings.json', - tokenDownloadUrl: 'https://raw.githubusercontent.com/Cockatrice/Magic-Token/master/tokens.xml' - }, -}); - -export default connect(mapStateToProps)(reduxForm(propsMap)(CardImportForm)); diff --git a/webclient/src/forms/KnownHostForm/KnownHostForm.css b/webclient/src/forms/KnownHostForm/KnownHostForm.css deleted file mode 100644 index 76e1722e0..000000000 --- a/webclient/src/forms/KnownHostForm/KnownHostForm.css +++ /dev/null @@ -1,21 +0,0 @@ -.KnownHostForm { - width: 100%; -} - -.KnownHostForm-item { - display: flex; - flex-direction: column; - margin-bottom: 20px; -} - -.KnownHostForm-submit { - width: 100%; -} - -.KnownHostForm-actions { - display: flex; - justify-content: space-between; - align-items: center; - margin: 5px 0 10px; - color: red; -} diff --git a/webclient/src/forms/KnownHostForm/KnownHostForm.i18n.json b/webclient/src/forms/KnownHostForm/KnownHostForm.i18n.json deleted file mode 100644 index fe614a126..000000000 --- a/webclient/src/forms/KnownHostForm/KnownHostForm.i18n.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - } -} diff --git a/webclient/src/forms/KnownHostForm/KnownHostForm.tsx b/webclient/src/forms/KnownHostForm/KnownHostForm.tsx deleted file mode 100644 index 1241336f3..000000000 --- a/webclient/src/forms/KnownHostForm/KnownHostForm.tsx +++ /dev/null @@ -1,94 +0,0 @@ -// eslint-disable-next-line -import React, { useState } from "react"; -import { connect } from 'react-redux'; -import { Form, Field } from 'react-final-form' -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; -import AnchorLink from '@mui/material/Link'; - -import { InputField } from 'components'; - -import './KnownHostForm.css'; - -const KnownHostForm = ({ host, onRemove, onSubmit }) => { - const [confirmDelete, setConfirmDelete] = useState(false); - const { t } = useTranslation(); - - const validate = values => { - const errors: any = {}; - - if (!values.name) { - errors.name = t('Common.validation.required'); - } - - if (!values.host) { - errors.host = t('Common.validation.required'); - } - - if (!values.port) { - errors.port = t('Common.validation.required'); - } - - if (Object.keys(errors).length) { - return errors; - } - }; - - const handleOnSubmit = ({ name, host, ...values }) => { - name = name?.trim(); - host = host?.trim(); - - onSubmit({ name, host, ...values }); - } - - return ( -
- {({ handleSubmit }) => ( - -
- -
-
- -
-
- -
- - - -
-
- { host && ( - - ) } -
- - { t('KnownHostForm.label.find') } - -
-
- ) } - - ); -}; - -const mapStateToProps = () => ({ - -}); - -export default connect(mapStateToProps)(KnownHostForm); diff --git a/webclient/src/forms/LoginForm/LoginForm.css b/webclient/src/forms/LoginForm/LoginForm.css deleted file mode 100644 index 4b176476f..000000000 --- a/webclient/src/forms/LoginForm/LoginForm.css +++ /dev/null @@ -1,20 +0,0 @@ -.loginForm { - width: 100%; -} - -.loginForm-item { - margin-bottom: 20px; -} - -.loginForm-actions { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: -20px; - margin-bottom: 20px; - font-weight: bold; -} - -.loginForm-submit { - width: 100%; -} diff --git a/webclient/src/forms/LoginForm/LoginForm.i18n.json b/webclient/src/forms/LoginForm/LoginForm.i18n.json deleted file mode 100644 index 260b88b27..000000000 --- a/webclient/src/forms/LoginForm/LoginForm.i18n.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Login", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - } -} diff --git a/webclient/src/forms/LoginForm/LoginForm.tsx b/webclient/src/forms/LoginForm/LoginForm.tsx deleted file mode 100644 index 1e8f104fe..000000000 --- a/webclient/src/forms/LoginForm/LoginForm.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import { Form, Field } from 'react-final-form'; -import { OnChange } from 'react-final-form-listeners'; -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; - -import { AuthenticationService } from 'api'; -import { CheckboxField, InputField, KnownHosts } from 'components'; -import { useAutoConnect } from 'hooks'; -import { HostDTO, SettingDTO } from 'services'; -import { APP_USER } from 'types'; - -import './LoginForm.css'; - -const LoginForm = ({ onSubmit, disableSubmitButton, onResetPassword }: LoginFormProps) => { - const { t } = useTranslation(); - const PASSWORD_LABEL = t('Common.label.password'); - const STORED_PASSWORD_LABEL = `* ${t('LoginForm.label.savedPassword')} *`; - - const [host, setHost] = useState(null); - const [useStoredPasswordLabel, setUseStoredPasswordLabel] = useState(false); - const [autoConnect, setAutoConnect] = useAutoConnect(); - - const validate = values => { - const errors: any = {}; - - if (!values.userName) { - errors.userName = t('Common.validation.required'); - } - if (!values.selectedHost) { - errors.selectedHost = t('Common.validation.required'); - } - - return errors; - } - - const useStoredPassword = (remember, password) => remember && host?.hashedPassword && !password; - const togglePasswordLabel = (useStoredLabel) => { - setUseStoredPasswordLabel(useStoredLabel); - }; - - const handleOnSubmit = ({ userName, ...values }) => { - userName = userName?.trim(); - console.log(userName, values); - - onSubmit({ userName, ...values }); - } - - return ( -
- {({ handleSubmit, form }) => { - const { values } = form.getState(); - - useEffect(() => { - SettingDTO.get(APP_USER).then((userSetting: SettingDTO) => { - if (userSetting?.autoConnect && !AuthenticationService.connectionAttemptMade()) { - HostDTO.getAll().then(hosts => { - let lastSelectedHost = hosts.find(({ lastSelected }) => lastSelected); - - if (lastSelectedHost?.remember && lastSelectedHost?.hashedPassword) { - togglePasswordLabel(true); - - form.change('selectedHost', lastSelectedHost); - form.change('userName', lastSelectedHost.userName); - form.change('remember', true); - form.submit(); - } - }); - } - }); - }, []); - - useEffect(() => { - if (!host) { - return; - } - - form.change('userName', host.userName); - form.change('password', ''); - - onRememberChange(host.remember); - onAutoConnectChange(host.remember && autoConnect); - togglePasswordLabel(useStoredPassword(host.remember, values.password)); - }, [host]); - - const onUserNameChange = (userName) => { - const fieldChanged = host?.userName?.toLowerCase() !== values.userName?.toLowerCase(); - if (useStoredPassword(values.remember, values.password) && fieldChanged) { - setHost(({ hashedPassword, ...s }) => ({ ...s, userName })); - } - } - - const onRememberChange = (checked) => { - form.change('remember', checked); - - if (!checked && values.autoConnect) { - onAutoConnectChange(false); - } - - togglePasswordLabel(useStoredPassword(checked, values.password)); - } - - const onAutoConnectChange = (checked) => { - setAutoConnect(checked); - - form.change('autoConnect', checked); - - if (checked && !values.remember) { - form.change('remember', true); - } - } - - return ( - -
-
- - {onUserNameChange} -
-
- setUseStoredPasswordLabel(false)} - onBlur={() => togglePasswordLabel(useStoredPassword(values.remember, values.password))} - name='password' - type='password' - component={InputField} - autoComplete='new-password' - /> -
-
- - {onRememberChange} - - -
-
- - {setHost} -
-
- - {onAutoConnectChange} -
-
- -
- ) - }} - - ); -}; - -interface LoginFormProps { - onSubmit: any; - disableSubmitButton: boolean, - onResetPassword: any; -} - -export default LoginForm; diff --git a/webclient/src/forms/RegisterForm/RegisterForm.css b/webclient/src/forms/RegisterForm/RegisterForm.css deleted file mode 100644 index 324aa7e63..000000000 --- a/webclient/src/forms/RegisterForm/RegisterForm.css +++ /dev/null @@ -1,18 +0,0 @@ -.RegisterForm { - width: 100%; - display: flex; - flex-direction: row; - justify-content: space-between; -} - -.RegisterForm-column { - width: 48%; -} - -.RegisterForm-item { - margin-bottom: 20px; -} - -.RegisterForm-submit { - width: 100%; -} diff --git a/webclient/src/forms/RegisterForm/RegisterForm.i18n.json b/webclient/src/forms/RegisterForm/RegisterForm.i18n.json deleted file mode 100644 index 50e802ea5..000000000 --- a/webclient/src/forms/RegisterForm/RegisterForm.i18n.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - } -} diff --git a/webclient/src/forms/RegisterForm/RegisterForm.tsx b/webclient/src/forms/RegisterForm/RegisterForm.tsx deleted file mode 100644 index f5f4d9174..000000000 --- a/webclient/src/forms/RegisterForm/RegisterForm.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { useState } from 'react'; -import { Form, Field } from 'react-final-form'; -import { OnChange } from 'react-final-form-listeners'; -import setFieldTouched from 'final-form-set-field-touched'; -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; -import Typography from '@mui/material/Typography'; - -import { CountryDropdown, InputField, KnownHosts } from 'components'; -import { useReduxEffect } from 'hooks'; -import { ServerTypes } from 'store'; - -import './RegisterForm.css'; -import { useToast } from 'components/Toast'; - -const RegisterForm = ({ onSubmit }: RegisterFormProps) => { - const { t } = useTranslation(); - const [emailRequired, setEmailRequired] = useState(false); - const [error, setError] = useState(null); - const [emailError, setEmailError] = useState(null); - const [passwordError, setPasswordError] = useState(null); - const [userNameError, setUserNameError] = useState(null); - const { openToast } = useToast({ key: 'registration-success', children: t('RegisterForm.toast.registerSuccess') }) - - const onHostChange = (host) => setEmailRequired(false); - const onEmailChange = () => emailError && setEmailError(null); - const onPasswordChange = () => passwordError && setPasswordError(null); - const onUserNameChange = () => userNameError && setUserNameError(null); - - useReduxEffect(() => { - setEmailRequired(true); - }, ServerTypes.REGISTRATION_REQUIRES_EMAIL); - - useReduxEffect(({ error }) => { - setError(error); - }, ServerTypes.REGISTRATION_FAILED); - - useReduxEffect(() => { - openToast() - }, ServerTypes.REGISTRATION_SUCCES); - - useReduxEffect(({ error }) => { - setEmailError(error); - }, ServerTypes.REGISTRATION_EMAIL_ERROR); - - useReduxEffect(({ error }) => { - setPasswordError(error); - }, ServerTypes.REGISTRATION_PASSWORD_ERROR); - - useReduxEffect(({ error }) => { - setUserNameError(error); - }, ServerTypes.REGISTRATION_USERNAME_ERROR); - - const handleOnSubmit = ({ userName, email, realName, ...values }) => { - setError(null); - - userName = userName?.trim(); - email = email?.trim(); - realName = realName?.trim(); - - onSubmit({ userName, email, realName, ...values }); - } - - const validate = values => { - const errors: any = {}; - - if (!values.userName) { - errors.userName = t('Common.validation.required'); - } else if (userNameError) { - errors.userName = userNameError; - } - - if (!values.password) { - errors.password = t('Common.validation.required'); - } else if (values.password.length < 8) { - errors.password = t('Common.validation.minChars', { count: 8 }); - } else if (passwordError) { - errors.password = passwordError; - } - - if (!values.passwordConfirm) { - errors.passwordConfirm = t('Common.validation.required'); - } else if (values.password !== values.passwordConfirm) { - errors.passwordConfirm = t('Common.validation.passwordsMustMatch'); - } - - if (!values.selectedHost) { - errors.selectedHost = t('Common.validation.required'); - } - - if (emailRequired && !values.email) { - errors.email = t('Common.validation.required'); - } else if (emailError) { - errors.email = emailError; - } - - return errors; - } - - return ( -
- {({ handleSubmit, form, ...args }) => { - const { values } = form.getState(); - - if (emailRequired) { - // Allow form render to complete - setTimeout(() => form.mutators.setFieldTouched('email', true)) - } - - return ( - <> - -
-
- - {onUserNameChange} -
-
- - {onPasswordChange} -
-
- -
-
- - {onHostChange} -
-
-
-
- -
-
- - {onEmailChange} -
-
- -
- -
-
- - { error && ( -
- {error} -
- )} - - ); - }} - - - ); -}; - -interface RegisterFormProps { - onSubmit: any; -} - -export default RegisterForm; diff --git a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.css b/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.css deleted file mode 100644 index 83ed7420f..000000000 --- a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.css +++ /dev/null @@ -1,25 +0,0 @@ -.RequestPasswordResetForm { - width: 100%; - padding-bottom: 15px; -} - -.RequestPasswordResetForm-item { - margin-bottom: 20px; -} - -.RequestPasswordResetForm-actions { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: -20px; - margin-bottom: 20px; - font-weight: bold; -} - -.RequestPasswordResetForm-submit { - width: 100%; -} - -.selectedHost { - margin-top: 40px; -} diff --git a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.i18n.json b/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.i18n.json deleted file mode 100644 index c53f6d097..000000000 --- a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.i18n.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - } -} diff --git a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.tsx b/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.tsx deleted file mode 100644 index 01d4a2959..000000000 --- a/webclient/src/forms/RequestPasswordResetForm/RequestPasswordResetForm.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// eslint-disable-next-line -import React, { useState } from "react"; -import { connect } from 'react-redux'; -import { Form, Field } from 'react-final-form'; -import { OnChange } from 'react-final-form-listeners'; -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; -import Typography from '@mui/material/Typography'; - -import { InputField, KnownHosts } from 'components'; -import { FormKey } from 'types'; - -import './RequestPasswordResetForm.css'; -import { useReduxEffect } from 'hooks'; -import { ServerTypes } from 'store'; - -const RequestPasswordResetForm = ({ onSubmit, skipTokenRequest }) => { - const [errorMessage, setErrorMessage] = useState(false); - const [isMFA, setIsMFA] = useState(false); - const { t } = useTranslation(); - - useReduxEffect(() => { - setErrorMessage(true); - }, ServerTypes.RESET_PASSWORD_FAILED, []); - - useReduxEffect(() => { - setIsMFA(true); - }, ServerTypes.RESET_PASSWORD_CHALLENGE, []); - - const handleOnSubmit = ({ userName, email, ...values }) => { - setErrorMessage(false); - - userName = userName?.trim(); - email = email?.trim(); - - onSubmit({ userName, email, ...values }); - } - - const validate = values => { - const errors: any = {}; - - if (!values.userName) { - errors.userName = t('Common.validation.required'); - } - if (isMFA && !values.email) { - errors.email = t('Common.validation.required'); - } - if (!values.selectedHost) { - errors.selectedHost = t('Common.validation.required'); - } - - return errors; - }; - - return ( -
- {({ handleSubmit, form }) => { - const onHostChange: any = ({ userName }) => { - form.change('userName', userName); - setIsMFA(false); - } - - return ( - -
-
- -
- {isMFA ? ( -
- -
{ t('RequestPasswordResetForm.mfaEnabled') }
-
- ) : null} -
- - {onHostChange} -
- - {errorMessage && ( -
- { t('RequestPasswordResetForm.error') } -
- )} -
- - - -
- -
-
- ); - }} - - ); -}; - -export default RequestPasswordResetForm; diff --git a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.css b/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.css deleted file mode 100644 index ad82b7efd..000000000 --- a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.css +++ /dev/null @@ -1,21 +0,0 @@ -.ResetPasswordForm { - width: 100%; - padding-bottom: 15px; -} - -.ResetPasswordForm-item { - margin-bottom: 20px; -} - -.ResetPasswordForm-actions { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: -20px; - margin-bottom: 20px; - font-weight: bold; -} - -.ResetPasswordForm-submit { - width: 100%; -} diff --git a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.i18n.json b/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.i18n.json deleted file mode 100644 index 2835134c0..000000000 --- a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.i18n.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Reset Password" - } - } -} diff --git a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx b/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx deleted file mode 100644 index 8b56f4b69..000000000 --- a/webclient/src/forms/ResetPasswordForm/ResetPasswordForm.tsx +++ /dev/null @@ -1,115 +0,0 @@ -// eslint-disable-next-line -import React, { useEffect, useState } from 'react'; -import { connect } from 'react-redux'; -import { Form, Field } from 'react-final-form' -import { OnChange } from 'react-final-form-listeners'; -import { useTranslation } from 'react-i18next'; - -import Button from '@mui/material/Button'; -import Typography from '@mui/material/Typography'; - -import { InputField, KnownHosts } from 'components'; -import { FormKey } from 'types'; - -import './ResetPasswordForm.css'; -import { useReduxEffect } from '../../hooks'; -import { ServerTypes } from '../../store'; - -const ResetPasswordForm = ({ onSubmit, userName }) => { - const [errorMessage, setErrorMessage] = useState(false); - const { t } = useTranslation(); - - useReduxEffect(() => { - setErrorMessage(true); - }, ServerTypes.RESET_PASSWORD_FAILED, []); - - const validate = values => { - const errors: any = {}; - - if (!values.userName) { - errors.userName = t('Common.validation.required'); - } - if (!values.token) { - errors.token = t('Common.validation.required'); - } - - if (!values.newPassword) { - errors.newPassword = t('Common.validation.required'); - } else if (values.newPassword.length < 8) { - errors.newPassword = t('Common.validation.minChars', { count: 8 }); - } - - if (!values.passwordAgain) { - errors.passwordAgain = t('Common.validation.required'); - } else if (values.newPassword !== values.passwordAgain) { - errors.passwordAgain = t('Common.validation.passwordsMustMatch'); - } - if (!values.selectedHost) { - errors.selectedHost = t('Common.validation.required'); - } - - return errors; - }; - - const handleOnSubmit = ({ userName, token, ...values }) => { - userName = userName?.trim(); - token = token?.trim(); - - onSubmit({ userName, token, ...values }); - } - - return ( -
- {({ handleSubmit, form }) => ( - -
-
- -
-
- -
-
- -
-
- -
-
- -
- - {errorMessage && ( -
- { t('ResetPasswordForm.error') } -
- )} -
- -
- )} - - ); -}; - -export default ResetPasswordForm; diff --git a/webclient/src/forms/SearchForm/SearchForm.css b/webclient/src/forms/SearchForm/SearchForm.css deleted file mode 100644 index 9e3cd6cb8..000000000 --- a/webclient/src/forms/SearchForm/SearchForm.css +++ /dev/null @@ -1,35 +0,0 @@ -.log-search { - margin-bottom: 20px; -} - -hr.MuiDivider-root { - margin: 20px 0; -} - -.log-search__form { - width: 100%; - padding: 20px; -} - -.log-search__form-item { - display: flex; -} - -.log-search__form-item.log-location { - display: flex; - justify-content: space-around; -} - -.log-search__form-item.log-location .checkbox-field { - display: flex; - flex-direction: column; -} - -.log-search__form-item.log-location .checkbox-field__box { - order: 1; -} - -.log-search__form-submit.MuiButton-root { - display: block; - margin: 0 auto; -} diff --git a/webclient/src/forms/SearchForm/SearchForm.tsx b/webclient/src/forms/SearchForm/SearchForm.tsx deleted file mode 100644 index 793543086..000000000 --- a/webclient/src/forms/SearchForm/SearchForm.tsx +++ /dev/null @@ -1,63 +0,0 @@ -// eslint-disable-next-line -import React, { Component } from "react"; -import { connect } from 'react-redux'; -import { Form, Field, reduxForm } from 'redux-form' - -import Button from '@mui/material/Button'; -import Divider from '@mui/material/Divider'; -import Paper from '@mui/material/Paper'; - -import { InputField, CheckboxField } from 'components'; -import { FormKey } from 'types'; - -import './SearchForm.css'; - -const SearchForm = ({ handleSubmit }) => ( - -
-
- -
-
- -
-
- -
-
- -
-
- -
- -
- - - -
- -
- Date Range: Coming Soon -
- -
- Maximum Results: 1000 -
- - - -
-); - -const propsMap = { - form: FormKey.SEARCH_LOGS, -}; - -const mapStateToProps = () => ({ - -}); - -export default connect(mapStateToProps)(reduxForm(propsMap)(SearchForm)); diff --git a/webclient/src/forms/index.ts b/webclient/src/forms/index.ts deleted file mode 100644 index b922a9f40..000000000 --- a/webclient/src/forms/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { default as AccountActivationForm } from './AccountActivationForm/AccountActivationForm'; -export { default as CardImportForm } from './CardImportForm/CardImportForm'; -export { default as LoginForm } from './LoginForm/LoginForm'; -export { default as KnownHostForm } from './KnownHostForm/KnownHostForm'; -export { default as RegisterForm } from './RegisterForm/RegisterForm'; -export { default as SearchForm } from './SearchForm/SearchForm'; -export { default as RequestPasswordResetForm } from './RequestPasswordResetForm/RequestPasswordResetForm'; -export { default as ResetPasswordForm } from './ResetPasswordForm/ResetPasswordForm'; diff --git a/webclient/src/hooks/index.ts b/webclient/src/hooks/index.ts deleted file mode 100644 index b5e9cca55..000000000 --- a/webclient/src/hooks/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './useAutoConnect'; -export * from './useFireOnce'; -export * from './useDebounce'; -export * from './useLocaleSort'; -export * from './useReduxEffect'; diff --git a/webclient/src/hooks/useAutoConnect.ts b/webclient/src/hooks/useAutoConnect.ts deleted file mode 100644 index 5258f58a7..000000000 --- a/webclient/src/hooks/useAutoConnect.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useEffect, useState } from 'react'; -import { debounce, DebouncedFunc } from 'lodash'; - -import { SettingDTO } from 'services'; -import { APP_USER } from 'types'; - -type OnChange = () => void; - -export function useAutoConnect() { - const [setting, setSetting] = useState(undefined); - const [autoConnect, setAutoConnect] = useState(undefined); - - useEffect(() => { - SettingDTO.get(APP_USER).then((setting: SettingDTO) => { - if (!setting) { - setting = new SettingDTO(APP_USER); - setting.save(); - } - - setSetting(setting); - }); - }, []); - - useEffect(() => { - if (setting) { - setAutoConnect(setting.autoConnect); - } - }, [setting]); - - useEffect(() => { - if (setting) { - setting.autoConnect = autoConnect; - setting.save(); - } - }, [setting, autoConnect]); - - return [autoConnect, setAutoConnect]; -} diff --git a/webclient/src/hooks/useDebounce.ts b/webclient/src/hooks/useDebounce.ts deleted file mode 100644 index 65aee9982..000000000 --- a/webclient/src/hooks/useDebounce.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useCallback } from 'react'; -import { debounce, DebouncedFunc } from 'lodash'; - -type UseDebounceType = (...args: any) => any; -const DEBOUNCE_DELAY = 250; - -export function useDebounce( - fn: T, - deps: any[] = [], - timeout: number = DEBOUNCE_DELAY -): DebouncedFunc { - return useCallback(debounce(fn, timeout), deps); -} diff --git a/webclient/src/hooks/useFireOnce/index.ts b/webclient/src/hooks/useFireOnce/index.ts deleted file mode 100644 index 442da884f..000000000 --- a/webclient/src/hooks/useFireOnce/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './useFireOnce' diff --git a/webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx b/webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx deleted file mode 100644 index 2f6ed34e0..000000000 --- a/webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { - render, - fireEvent, - getByRole, - waitFor, - act -} from '@testing-library/react'; -import { useFireOnce } from './useFireOnce'; - -describe('useFireOnce hook', () => { - test('it only fires once when button is clicked twice', async () => { - // Mock a promise with a delay - const onClickWithPromise = jest.fn((e) => { - e.preventDefault() - return new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, 100); - }); - }); - - function Button(props) { - const { children, onClick } = props - const [buttonIsDisabled, setButtonIsDisabled, handleClickOnce] = useFireOnce(onClick) - return - } - - // render the button - const { getByRole } = render( - - ); - - //Grab the button from the DOM and confirm it initialized in an enabled state - const button = getByRole('button', { name: 'Click Me!' }); - expect(button).toBeEnabled(); - - // Simulate two click events in a row - fireEvent.click(button); - fireEvent.click(button); - - // Confirm that it's disabled - await waitFor(() => { - expect(button).toBeDisabled(); - }); - - // Confirm it became enabled after the timeout and that the click event was only fired once - await waitFor( - () => { - expect(onClickWithPromise).toHaveBeenCalledTimes(1); - }, - { timeout: 100 } - ); - }); - - test('it only fires once when form is submitted twice', async () => { - // Mock a promise with a delay - const onClickWithPromise = jest.fn((e) => { - e.preventDefault() - return new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, 100); - }); - }); - - function Form(props) { - const { onSubmit } = props - const [buttonIsDisabled, setButtonIsDisabled, handleSubmitOnce] = useFireOnce(onSubmit) - return ( -
- - -
- ) - } - - // render the form - const { getByRole } = render( -
- ); - - //Grab the button from the DOM and confirm it initialized in an enabled state - const button = getByRole('button', { name: 'Click Me!' }); - expect(button).toBeEnabled(); - - // Simulate two click events in a row - fireEvent.click(button); - fireEvent.click(button); - - // Confirm that it's disabled - await waitFor(() => { - expect(button).toBeDisabled(); - }); - - // Confirm it became enabled after the timeout and that the click event was only fired once - await waitFor( - () => { - expect(onClickWithPromise).toHaveBeenCalledTimes(1); - }, - { timeout: 100 } - ); - }); -}); diff --git a/webclient/src/hooks/useFireOnce/useFireOnce.ts b/webclient/src/hooks/useFireOnce/useFireOnce.ts deleted file mode 100644 index 0b042a907..000000000 --- a/webclient/src/hooks/useFireOnce/useFireOnce.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { useCallback, useState } from 'react'; -import { useReduxEffect } from 'hooks'; -import { ServerTypes } from 'store'; - -type UseFireOnceType = (...args: any) => any; - -export function useFireOnce(fn: T): [boolean, any, any] { - const [actionIsInFlight, setActionIsInFlight] = useState(false) - const handleFireOnce = useCallback((args) => { - setActionIsInFlight(true); - fn(args); - }, []) - function resetInFlightStatus() { - setActionIsInFlight(false); - } - return [actionIsInFlight, resetInFlightStatus, handleFireOnce] -} diff --git a/webclient/src/hooks/useLocaleSort.ts b/webclient/src/hooks/useLocaleSort.ts deleted file mode 100644 index 219292ed7..000000000 --- a/webclient/src/hooks/useLocaleSort.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -export function useLocaleSort(arr: string[], valueGetter: (value: string) => string) { - const [state] = useState(arr); - const [sorted, setSorted] = useState([]); - - const { i18n } = useTranslation(); - - useEffect(() => { - const collator = new Intl.Collator(i18n.language); - const sorter = (a, b) => collator.compare(valueGetter(a), valueGetter(b)); - - setSorted(state.sort(sorter)); - }, [state, i18n.language]); - - return sorted; -} diff --git a/webclient/src/hooks/useReduxEffect.tsx b/webclient/src/hooks/useReduxEffect.tsx deleted file mode 100644 index 83273a6d3..000000000 --- a/webclient/src/hooks/useReduxEffect.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/** -File is adapted from https://github.com/Qeepsake/use-redux-effect under MIT License - * @author Aspect Apps Limited - * @description - */ - -import { useRef, useEffect, DependencyList } from 'react' -import { useStore } from 'react-redux' -import { AnyAction } from 'redux' -import { castArray } from 'lodash' - -export type ReduxEffect = (action: AnyAction) => void - -/** - * Subscribes to redux store events - * - * @param effect - * @param type - * @param deps - */ -export function useReduxEffect( - effect: ReduxEffect, - type: string | string[], - deps: DependencyList = [], -): void { - const currentValue = useRef(null); - const store = useStore(); - - const handleChange = (): void => { - const state: any = store.getState(); - const action = state.action; - const previousValue = currentValue.current; - currentValue.current = action.count; - - if ( - previousValue !== action.count && - castArray(type).includes(action.type) - ) { - effect(action); - } - } - - useEffect(() => { - const unsubscribe = store.subscribe(handleChange); - return (): void => unsubscribe(); - }, deps) -} diff --git a/webclient/src/i18n-backend.ts b/webclient/src/i18n-backend.ts deleted file mode 100644 index d270b3503..000000000 --- a/webclient/src/i18n-backend.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ModuleType } from 'i18next'; - -import { Language } from 'types'; - -class I18nBackend { - static type: ModuleType = 'backend'; - static BASE_URL = `${process.env.PUBLIC_URL}/locales`; - - read(language, namespace, callback) { - if (!Language[language]) { - callback(true, null); - return; - } - - fetch(`${I18nBackend.BASE_URL}/${Language[language]}/${namespace}.json`) - .then(resp => resp.json().then(json => callback(null, json))) - .catch(error => callback(error, null)); - } -} - -export default I18nBackend; diff --git a/webclient/src/i18n-default.json b/webclient/src/i18n-default.json deleted file mode 100644 index 3c63e0abb..000000000 --- a/webclient/src/i18n-default.json +++ /dev/null @@ -1,382 +0,0 @@ -{ - "Common": { - "language": "English", - "disconnect": "Disconnect", - "label": { - "confirmPassword": "Confirm Password", - "confirmSure": "Are you sure?", - "country": "Country", - "delete": "Delete", - "email": "Email", - "hostName": "Host Name", - "hostAddress": "Host Address", - "password": "Password", - "passwordAgain": "Password Again", - "port": "Port", - "realName": "Real Name", - "saveChanges": "Save Changes", - "token": "Token", - "username": "Username" - }, - "validation": { - "minChars": "Minimum of {count} {count, plural, one {character} other {characters}} required", - "passwordsMustMatch": "Passwords don't match", - "required": "Required" - }, - "countries": { - "AD": "Andorra", - "AE": "United Arab Emirates", - "AF": "Afghanistan", - "AG": "Antigua and Barbuda", - "AI": "Anguilla", - "AL": "Albania", - "AM": "Armenia", - "AO": "Angola", - "AQ": "Antarctica", - "AR": "Argentina", - "AS": "American Samoa", - "AT": "Austria", - "AU": "Australia", - "AW": "Aruba", - "AX": "Åland Islands", - "AZ": "Azerbaijan", - "BA": "Bosnia and Herzegovina", - "BB": "Barbados", - "BD": "Bangladesh", - "BE": "Belgium", - "BF": "Burkina Faso", - "BG": "Bulgaria", - "BH": "Bahrain", - "BI": "Burundi", - "BJ": "Benin", - "BL": "Saint Barthélemy", - "BM": "Bermuda", - "BN": "Brunei Darussalam", - "BO": "Bolivia", - "BQ": "Bonaire, Sint Eustatius and Saba", - "BR": "Brazil", - "BS": "Bahamas", - "BT": "Bhutan", - "BV": "Bouvet Island", - "BW": "Botswana", - "BY": "Belarus", - "BZ": "Belize", - "CA": "Canada", - "CC": "Cocos (Keeling) Islands", - "CD": "DR Congo", - "CF": "Central African Republic", - "CG": "Republic of the Congo", - "CH": "Switzerland", - "CI": "Ivory Coast", - "CK": "Cook Islands", - "CL": "Chile", - "CM": "Cameroon", - "CN": "China", - "CO": "Colombia", - "CR": "Costa Rica", - "CU": "Cuba", - "CV": "Cape Verde", - "CW": "Curaçao", - "CX": "Christmas Island", - "CY": "Cyprus", - "CZ": "Czechia", - "DE": "Germany", - "DJ": "Djibouti", - "DK": "Denmark", - "DM": "Dominica", - "DO": "Dominican Republic", - "DZ": "Algeria", - "EC": "Ecuador", - "EE": "Estonia", - "EG": "Egypt", - "EH": "Western Sahara", - "ER": "Eritrea", - "ES": "Spain", - "ET": "Ethiopia", - "FI": "Finland", - "FJ": "Fiji", - "FK": "Falkland Islands", - "FM": "Micronesia", - "FO": "Faroe Islands", - "FR": "France", - "GA": "Gabon", - "GB": "United Kingdom", - "GD": "Grenada", - "GE": "Georgia", - "GF": "French Guiana", - "GG": "Guernsey", - "GH": "Ghana", - "GI": "Gibraltar", - "GL": "Greenland", - "GM": "Gambia", - "GN": "Guinea", - "GP": "Guadeloupe", - "GQ": "Equatorial Guinea", - "GR": "Greece", - "GS": "South Georgia and the South Sandwich Islands", - "GT": "Guatemala", - "GU": "Guam", - "GW": "Guinea-Bissau", - "GY": "Guyana", - "HK": "Hong Kong", - "HM": "Heard Island and McDonald Islands", - "HN": "Honduras", - "HR": "Croatia", - "HT": "Haiti", - "HU": "Hungary", - "ID": "Indonesia", - "IE": "Ireland", - "IL": "Israel", - "IM": "Isle of Man", - "IN": "India", - "IO": "British Indian Ocean Territory", - "IQ": "Iraq", - "IR": "Iran", - "IS": "Iceland", - "IT": "Italy", - "JE": "Jersey", - "JM": "Jamaica", - "JO": "Jordan", - "JP": "Japan", - "KE": "Kenya", - "KG": "Kyrgyzstan", - "KH": "Cambodia", - "KI": "Kiribati", - "KM": "Comoros", - "KN": "Saint Kitts and Nevis", - "KP": "North Korea", - "KR": "South Korea", - "KW": "Kuwait", - "KY": "Cayman Islands", - "KZ": "Kazakhstan", - "LA": "Laos", - "LB": "Lebanon", - "LC": "Saint Lucia", - "LI": "Liechtenstein", - "LK": "Sri Lanka", - "LR": "Liberia", - "LS": "Lesotho", - "LT": "Lithuania", - "LU": "Luxembourg", - "LV": "Latvia", - "LY": "Libya", - "MA": "Morocco", - "MC": "Monaco", - "MD": "Moldova", - "ME": "Montenegro", - "MF": "Saint Martin (French part)", - "MG": "Madagascar", - "MH": "Marshall Islands", - "MK": "North Macedonia", - "ML": "Mali", - "MM": "Myanmar", - "MN": "Mongolia", - "MO": "Macao", - "MP": "Northern Mariana Islands", - "MQ": "Martinique", - "MR": "Mauritania", - "MS": "Montserrat", - "MT": "Malta", - "MU": "Mauritius", - "MV": "Maldives", - "MW": "Malawi", - "MX": "Mexico", - "MY": "Malaysia", - "MZ": "Mozambique", - "NA": "Namibia", - "NC": "New Caledonia", - "NE": "Niger", - "NF": "Norfolk Island", - "NG": "Nigeria", - "NI": "Nicaragua", - "NL": "Netherlands", - "NO": "Norway", - "NP": "Nepal", - "NR": "Nauru", - "NU": "Niue", - "NZ": "New Zealand", - "OM": "Oman", - "PA": "Panama", - "PE": "Peru", - "PF": "French Polynesia", - "PG": "Papua New Guinea", - "PH": "Philippines", - "PK": "Pakistan", - "PL": "Poland", - "PM": "Saint Pierre and Miquelon", - "PN": "Pitcairn", - "PR": "Puerto Rico", - "PS": "Palestine", - "PT": "Portugal", - "PW": "Palau", - "PY": "Paraguay", - "QA": "Qatar", - "RE": "Réunion", - "RO": "Romania", - "RS": "Serbia", - "RU": "Russia", - "RW": "Rwanda", - "SA": "Saudi Arabia", - "SB": "Solomon Islands", - "SC": "Seychelles", - "SD": "Sudan", - "SE": "Sweden", - "SG": "Singapore", - "SH": "Saint Helena, Ascension and Tristan da Cunha", - "SI": "Slovenia", - "SJ": "Svalbard and Jan Mayen", - "SK": "Slovakia", - "SL": "Sierra Leone", - "SM": "San Marino", - "SN": "Senegal", - "SO": "Somalia", - "SR": "Suriname", - "SS": "South Sudan", - "ST": "Sao Tome and Principe", - "SV": "El Salvador", - "SX": "Sint Maarten (Dutch part)", - "SY": "Syria", - "SZ": "Eswatini", - "TC": "Turks and Caicos Islands", - "TD": "Chad", - "TF": "TAAF", - "TG": "Togo", - "TH": "Thailand", - "TJ": "Tajikistan", - "TK": "Tokelau", - "TL": "Timor-Leste", - "TM": "Turkmenistan", - "TN": "Tunisia", - "TO": "Tonga", - "TR": "Turkey", - "TT": "Trinidad and Tobago", - "TV": "Tuvalu", - "TW": "Taiwan", - "TZ": "Tanzania", - "UA": "Ukraine", - "UG": "Uganda", - "UM": "United States Minor Outlying Islands", - "US": "United States", - "UY": "Uruguay", - "UZ": "Uzbekistan", - "VA": "Holy See", - "VC": "Saint Vincent and the Grenadines", - "VE": "Venezuela", - "VG": "British Virgin Islands", - "VI": "U.S. Virgin Islands", - "VN": "Viet Nam", - "VU": "Vanuatu", - "WF": "Wallis and Futuna", - "WS": "Samoa", - "YE": "Yemen", - "YT": "Mayotte", - "XK": "Kosovo", - "ZA": "South Africa", - "ZM": "Zambia", - "ZW": "Zimbabwe", - "EU": "European Union" - }, - "languages": { - "en-US": "English - US", - "fr": "French", - "nl": "Dutch", - "pt_BR": "Portuguese - Brazil" - } - }, - "KnownHosts": { - "label": "Host", - "add": "Add new host", - "toast": "Host successfully {mode, select, created {created} deleted {deleted} other {edited}}." - }, - "InitializeContainer": { - "title": "DID YOU KNOW", - "subtitle": "<1>Cockatrice is run by volunteers<1>that love card games!" - }, - "LoginContainer": { - "header": { - "title": "Login", - "subtitle": "A cross-platform virtual tabletop for multiplayer card games." - }, - "footer": { - "registerPrompt": "Not registered yet?", - "registerAction": "Create an account", - "credit": "Cockatrice is an open source project", - "version": "Version" - }, - "content": { - "subtitle1": "Play multiplayer card games online.", - "subtitle2": "Cross-platform virtual tabletop for multiplayer card games. Forever free." - }, - "toasts": { - "passwordResetSuccessToast": "Password Reset Successfully", - "accountActivationSuccess": "Account Activated Successfully" - } - }, - "UnsupportedContainer": { - "title": "Unsupported Browser", - "subtitle1": "Please update your browser and/or check your permissions.", - "subtitle2": "Note: Private browsing causes some browsers to disable certain permissions or features." - }, - "AccountActivationDialog": { - "title": "Account Activation", - "subtitle1": "Your account has not been activated yet.", - "subtitle2": "You need to provide the activation token received in the activation email." - }, - "KnownHostDialog": { - "title": "{mode, select, edit {Edit} other {Add}} Known Host", - "subtitle": "Adding a new host allows you to connect to different servers. Enter the details below to your host list." - }, - "RegistrationDialog": { - "title": "Create New Account" - }, - "RequestPasswordResetDialog": { - "title": "Request Password Reset" - }, - "ResetPasswordDialog": { - "title": "Reset Password" - }, - "AccountActivationForm": { - "error": { - "failed": "Account activation failed" - }, - "label": { - "activate": "Activate Account" - } - }, - "KnownHostForm": { - "help": "Need help adding a new host?", - "label": { - "add": "Add Host", - "find": "Find Host" - } - }, - "LoginForm": { - "label": { - "autoConnect": "Auto Connect", - "forgot": "Forgot Password", - "login": "Login", - "savePassword": "Save Password", - "savedPassword": "Saved Password" - } - }, - "RegisterForm": { - "label": { - "register": "Register" - }, - "toast": { - "registerSuccess": "Registration Successful!" - } - }, - "RequestPasswordResetForm": { - "error": "Request password reset failed", - "mfaEnabled": "Server has multi-factor authentication enabled", - "request": "Request Reset Token", - "skipRequest": "I already have a reset token" - }, - "ResetPasswordForm": { - "error": "Password reset failed", - "label": { - "reset": "Reset Password" - } - } -} \ No newline at end of file diff --git a/webclient/src/i18n.ts b/webclient/src/i18n.ts deleted file mode 100644 index ef1885965..000000000 --- a/webclient/src/i18n.ts +++ /dev/null @@ -1,32 +0,0 @@ -import i18n from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; -import ICU from 'i18next-icu'; -import { initReactI18next } from 'react-i18next'; - -import { Language } from 'types'; - -import I18nBackend from './i18n-backend'; - -// Bundle default translation with application -import translation from './i18n-default.json'; - -i18n - .use(ICU) - .use(I18nBackend) - .use(LanguageDetector) - .use(initReactI18next) - // for all options read: https://www.i18next.com/overview/configuration-options - .init({ - fallbackLng: Language['en-US'], - resources: { - [Language['en-US']]: { translation }, - }, - partialBundledLanguages: true, - - interpolation: { - // not needed for react as it escapes by default - escapeValue: false, - } - }); - -export default i18n; diff --git a/webclient/src/images/Images.ts b/webclient/src/images/Images.ts deleted file mode 100644 index 9d08eb3ec..000000000 --- a/webclient/src/images/Images.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Countries } from './countries/_Countries'; -import { Faces } from './faces/_Faces'; -import Logo from './logo.png'; - -export class Images { - static Countries = Countries; - static Faces = Faces; - static Logo = Logo; -} diff --git a/webclient/src/images/countries/.gitignore b/webclient/src/images/countries/.gitignore deleted file mode 100644 index 1337edaa7..000000000 --- a/webclient/src/images/countries/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Ignore all files -* -# Except gitignore -!.gitignore - -!_Countries.ts diff --git a/webclient/src/images/countries/_Countries.ts b/webclient/src/images/countries/_Countries.ts deleted file mode 100644 index a4867db46..000000000 --- a/webclient/src/images/countries/_Countries.ts +++ /dev/null @@ -1,507 +0,0 @@ -// Remove !file-loader! once the following is no longer an issue -// https://github.com/facebook/create-react-app/issues/11770 -import ad from '!file-loader!./ad.svg'; -import ae from '!file-loader!./ae.svg'; -import af from '!file-loader!./af.svg'; -import ag from '!file-loader!./ag.svg'; -import ai from '!file-loader!./ai.svg'; -import al from '!file-loader!./al.svg'; -import am from '!file-loader!./am.svg'; -import ao from '!file-loader!./ao.svg'; -import aq from '!file-loader!./aq.svg'; -import ar from '!file-loader!./ar.svg'; -import as from '!file-loader!./as.svg'; -import at from '!file-loader!./at.svg'; -import au from '!file-loader!./au.svg'; -import aw from '!file-loader!./aw.svg'; -import ax from '!file-loader!./ax.svg'; -import az from '!file-loader!./az.svg'; -import ba from '!file-loader!./ba.svg'; -import bb from '!file-loader!./bb.svg'; -import bd from '!file-loader!./bd.svg'; -import be from '!file-loader!./be.svg'; -import bf from '!file-loader!./bf.svg'; -import bg from '!file-loader!./bg.svg'; -import bh from '!file-loader!./bh.svg'; -import bi from '!file-loader!./bi.svg'; -import bj from '!file-loader!./bj.svg'; -import bl from '!file-loader!./bl.svg'; -import bm from '!file-loader!./bm.svg'; -import bn from '!file-loader!./bn.svg'; -import bo from '!file-loader!./bo.svg'; -import bq from '!file-loader!./bq.svg'; -import br from '!file-loader!./br.svg'; -import bs from '!file-loader!./bs.svg'; -import bt from '!file-loader!./bt.svg'; -import bv from '!file-loader!./bv.svg'; -import bw from '!file-loader!./bw.svg'; -import by from '!file-loader!./by.svg'; -import bz from '!file-loader!./bz.svg'; -import ca from '!file-loader!./ca.svg'; -import cc from '!file-loader!./cc.svg'; -import cd from '!file-loader!./cd.svg'; -import cf from '!file-loader!./cf.svg'; -import cg from '!file-loader!./cg.svg'; -import ch from '!file-loader!./ch.svg'; -import ci from '!file-loader!./ci.svg'; -import ck from '!file-loader!./ck.svg'; -import cl from '!file-loader!./cl.svg'; -import cm from '!file-loader!./cm.svg'; -import cn from '!file-loader!./cn.svg'; -import co from '!file-loader!./co.svg'; -import cr from '!file-loader!./cr.svg'; -import cu from '!file-loader!./cu.svg'; -import cv from '!file-loader!./cv.svg'; -import cw from '!file-loader!./cw.svg'; -import cx from '!file-loader!./cx.svg'; -import cy from '!file-loader!./cy.svg'; -import cz from '!file-loader!./cz.svg'; -import de from '!file-loader!./de.svg'; -import dj from '!file-loader!./dj.svg'; -import dk from '!file-loader!./dk.svg'; -import dm from '!file-loader!./dm.svg'; -import _do from '!file-loader!./do.svg'; -import dz from '!file-loader!./dz.svg'; -import ec from '!file-loader!./ec.svg'; -import ee from '!file-loader!./ee.svg'; -import eg from '!file-loader!./eg.svg'; -import eh from '!file-loader!./eh.svg'; -import er from '!file-loader!./er.svg'; -import es from '!file-loader!./es.svg'; -import et from '!file-loader!./et.svg'; -import eu from '!file-loader!./eu.svg'; -import fi from '!file-loader!./fi.svg'; -import fj from '!file-loader!./fj.svg'; -import fk from '!file-loader!./fk.svg'; -import fm from '!file-loader!./fm.svg'; -import fo from '!file-loader!./fo.svg'; -import fr from '!file-loader!./fr.svg'; -import ga from '!file-loader!./ga.svg'; -import gb from '!file-loader!./gb.svg'; -import gd from '!file-loader!./gd.svg'; -import ge from '!file-loader!./ge.svg'; -import gf from '!file-loader!./gf.svg'; -import gg from '!file-loader!./gg.svg'; -import gh from '!file-loader!./gh.svg'; -import gi from '!file-loader!./gi.svg'; -import gl from '!file-loader!./gl.svg'; -import gm from '!file-loader!./gm.svg'; -import gn from '!file-loader!./gn.svg'; -import gp from '!file-loader!./gp.svg'; -import gq from '!file-loader!./gq.svg'; -import gr from '!file-loader!./gr.svg'; -import gs from '!file-loader!./gs.svg'; -import gt from '!file-loader!./gt.svg'; -import gu from '!file-loader!./gu.svg'; -import gw from '!file-loader!./gw.svg'; -import gy from '!file-loader!./gy.svg'; -import hk from '!file-loader!./hk.svg'; -import hm from '!file-loader!./hm.svg'; -import hn from '!file-loader!./hn.svg'; -import hr from '!file-loader!./hr.svg'; -import ht from '!file-loader!./ht.svg'; -import hu from '!file-loader!./hu.svg'; -import id from '!file-loader!./id.svg'; -import ie from '!file-loader!./ie.svg'; -import il from '!file-loader!./il.svg'; -import im from '!file-loader!./im.svg'; -import _in from '!file-loader!./in.svg'; -import io from '!file-loader!./io.svg'; -import iq from '!file-loader!./iq.svg'; -import ir from '!file-loader!./ir.svg'; -import is from '!file-loader!./is.svg'; -import it from '!file-loader!./it.svg'; -import je from '!file-loader!./je.svg'; -import jm from '!file-loader!./jm.svg'; -import jo from '!file-loader!./jo.svg'; -import jp from '!file-loader!./jp.svg'; -import ke from '!file-loader!./ke.svg'; -import kg from '!file-loader!./kg.svg'; -import kh from '!file-loader!./kh.svg'; -import ki from '!file-loader!./ki.svg'; -import km from '!file-loader!./km.svg'; -import kn from '!file-loader!./kn.svg'; -import kp from '!file-loader!./kp.svg'; -import kr from '!file-loader!./kr.svg'; -import kw from '!file-loader!./kw.svg'; -import ky from '!file-loader!./ky.svg'; -import kz from '!file-loader!./kz.svg'; -import la from '!file-loader!./la.svg'; -import lb from '!file-loader!./lb.svg'; -import lc from '!file-loader!./lc.svg'; -import li from '!file-loader!./li.svg'; -import lk from '!file-loader!./lk.svg'; -import lr from '!file-loader!./lr.svg'; -import ls from '!file-loader!./ls.svg'; -import lt from '!file-loader!./lt.svg'; -import lu from '!file-loader!./lu.svg'; -import lv from '!file-loader!./lv.svg'; -import ly from '!file-loader!./ly.svg'; -import ma from '!file-loader!./ma.svg'; -import mc from '!file-loader!./mc.svg'; -import md from '!file-loader!./md.svg'; -import me from '!file-loader!./me.svg'; -import mf from '!file-loader!./mf.svg'; -import mg from '!file-loader!./mg.svg'; -import mh from '!file-loader!./mh.svg'; -import mk from '!file-loader!./mk.svg'; -import ml from '!file-loader!./ml.svg'; -import mm from '!file-loader!./mm.svg'; -import mn from '!file-loader!./mn.svg'; -import mo from '!file-loader!./mo.svg'; -import mp from '!file-loader!./mp.svg'; -import mq from '!file-loader!./mq.svg'; -import mr from '!file-loader!./mr.svg'; -import ms from '!file-loader!./ms.svg'; -import mt from '!file-loader!./mt.svg'; -import mu from '!file-loader!./mu.svg'; -import mv from '!file-loader!./mv.svg'; -import mw from '!file-loader!./mw.svg'; -import mx from '!file-loader!./mx.svg'; -import my from '!file-loader!./my.svg'; -import mz from '!file-loader!./mz.svg'; -import na from '!file-loader!./na.svg'; -import nc from '!file-loader!./nc.svg'; -import ne from '!file-loader!./ne.svg'; -import nf from '!file-loader!./nf.svg'; -import ng from '!file-loader!./ng.svg'; -import ni from '!file-loader!./ni.svg'; -import nl from '!file-loader!./nl.svg'; -import no from '!file-loader!./no.svg'; -import np from '!file-loader!./np.svg'; -import nr from '!file-loader!./nr.svg'; -import nu from '!file-loader!./nu.svg'; -import nz from '!file-loader!./nz.svg'; -import om from '!file-loader!./om.svg'; -import pa from '!file-loader!./pa.svg'; -import pe from '!file-loader!./pe.svg'; -import pf from '!file-loader!./pf.svg'; -import pg from '!file-loader!./pg.svg'; -import ph from '!file-loader!./ph.svg'; -import pk from '!file-loader!./pk.svg'; -import pl from '!file-loader!./pl.svg'; -import pm from '!file-loader!./pm.svg'; -import pn from '!file-loader!./pn.svg'; -import pr from '!file-loader!./pr.svg'; -import ps from '!file-loader!./ps.svg'; -import pt from '!file-loader!./pt.svg'; -import pw from '!file-loader!./pw.svg'; -import py from '!file-loader!./py.svg'; -import qa from '!file-loader!./qa.svg'; -import re from '!file-loader!./re.svg'; -import ro from '!file-loader!./ro.svg'; -import rs from '!file-loader!./rs.svg'; -import ru from '!file-loader!./ru.svg'; -import rw from '!file-loader!./rw.svg'; -import sa from '!file-loader!./sa.svg'; -import sb from '!file-loader!./sb.svg'; -import sc from '!file-loader!./sc.svg'; -import sd from '!file-loader!./sd.svg'; -import se from '!file-loader!./se.svg'; -import sg from '!file-loader!./sg.svg'; -import sh from '!file-loader!./sh.svg'; -import si from '!file-loader!./si.svg'; -import sj from '!file-loader!./sj.svg'; -import sk from '!file-loader!./sk.svg'; -import sl from '!file-loader!./sl.svg'; -import sm from '!file-loader!./sm.svg'; -import sn from '!file-loader!./sn.svg'; -import so from '!file-loader!./so.svg'; -import sr from '!file-loader!./sr.svg'; -import ss from '!file-loader!./ss.svg'; -import st from '!file-loader!./st.svg'; -import sv from '!file-loader!./sv.svg'; -import sx from '!file-loader!./sx.svg'; -import sy from '!file-loader!./sy.svg'; -import sz from '!file-loader!./sz.svg'; -import tc from '!file-loader!./tc.svg'; -import td from '!file-loader!./td.svg'; -import tf from '!file-loader!./tf.svg'; -import tg from '!file-loader!./tg.svg'; -import th from '!file-loader!./th.svg'; -import tj from '!file-loader!./tj.svg'; -import tk from '!file-loader!./tk.svg'; -import tl from '!file-loader!./tl.svg'; -import tm from '!file-loader!./tm.svg'; -import tn from '!file-loader!./tn.svg'; -import to from '!file-loader!./to.svg'; -import tr from '!file-loader!./tr.svg'; -import tt from '!file-loader!./tt.svg'; -import tv from '!file-loader!./tv.svg'; -import tw from '!file-loader!./tw.svg'; -import tz from '!file-loader!./tz.svg'; -import ua from '!file-loader!./ua.svg'; -import ug from '!file-loader!./ug.svg'; -import um from '!file-loader!./um.svg'; -import us from '!file-loader!./us.svg'; -import uy from '!file-loader!./uy.svg'; -import uz from '!file-loader!./uz.svg'; -import va from '!file-loader!./va.svg'; -import vc from '!file-loader!./vc.svg'; -import ve from '!file-loader!./ve.svg'; -import vg from '!file-loader!./vg.svg'; -import vi from '!file-loader!./vi.svg'; -import vn from '!file-loader!./vn.svg'; -import vu from '!file-loader!./vu.svg'; -import wf from '!file-loader!./wf.svg'; -import ws from '!file-loader!./ws.svg'; -import xk from '!file-loader!./xk.svg'; -import ye from '!file-loader!./ye.svg'; -import yt from '!file-loader!./yt.svg'; -import za from '!file-loader!./za.svg'; -import zm from '!file-loader!./zm.svg'; -import zw from '!file-loader!./zw.svg'; - -export const Countries = { - ad, - ae, - af, - ag, - ai, - al, - am, - ao, - aq, - ar, - as, - at, - au, - aw, - ax, - az, - ba, - bb, - bd, - be, - bf, - bg, - bh, - bi, - bj, - bl, - bm, - bn, - bo, - bq, - br, - bs, - bt, - bv, - bw, - by, - bz, - ca, - cc, - cd, - cf, - cg, - ch, - ci, - ck, - cl, - cm, - cn, - co, - cr, - cu, - cv, - cw, - cx, - cy, - cz, - de, - dj, - dk, - dm, - do: _do, - dz, - ec, - ee, - eg, - eh, - er, - es, - et, - eu, - fi, - fj, - fk, - fm, - fo, - fr, - ga, - gb, - gd, - ge, - gf, - gg, - gh, - gi, - gl, - gm, - gn, - gp, - gq, - gr, - gs, - gt, - gu, - gw, - gy, - hk, - hm, - hn, - hr, - ht, - hu, - id, - ie, - il, - im, - in: _in, - io, - iq, - ir, - is, - it, - je, - jm, - jo, - jp, - ke, - kg, - kh, - ki, - km, - kn, - kp, - kr, - kw, - ky, - kz, - la, - lb, - lc, - li, - lk, - lr, - ls, - lt, - lu, - lv, - ly, - ma, - mc, - md, - me, - mf, - mg, - mh, - mk, - ml, - mm, - mn, - mo, - mp, - mq, - mr, - ms, - mt, - mu, - mv, - mw, - mx, - my, - mz, - na, - nc, - ne, - nf, - ng, - ni, - nl, - no, - np, - nr, - nu, - nz, - om, - pa, - pe, - pf, - pg, - ph, - pk, - pl, - pm, - pn, - pr, - ps, - pt, - pw, - py, - qa, - re, - ro, - rs, - ru, - rw, - sa, - sb, - sc, - sd, - se, - sg, - sh, - si, - sj, - sk, - sl, - sm, - sn, - so, - sr, - ss, - st, - sv, - sx, - sy, - sz, - tc, - td, - tf, - tg, - th, - tj, - tk, - tl, - tm, - tn, - to, - tr, - tt, - tv, - tw, - tz, - ua, - ug, - um, - us, - uy, - uz, - va, - vc, - ve, - vg, - vi, - vn, - vu, - wf, - ws, - xk, - ye, - yt, - za, - zm, - zw, -}; diff --git a/webclient/src/images/faces/_Faces.ts b/webclient/src/images/faces/_Faces.ts deleted file mode 100644 index 73a8c361b..000000000 --- a/webclient/src/images/faces/_Faces.ts +++ /dev/null @@ -1,9 +0,0 @@ -import face1 from './face1.jpg'; -import face2 from './face2.jpg'; -import face3 from './face3.jpg'; - -export const Faces = { - face1, - face2, - face3, -} diff --git a/webclient/src/images/faces/face1.jpg b/webclient/src/images/faces/face1.jpg deleted file mode 100644 index 931c5822d..000000000 Binary files a/webclient/src/images/faces/face1.jpg and /dev/null differ diff --git a/webclient/src/images/faces/face2.jpg b/webclient/src/images/faces/face2.jpg deleted file mode 100644 index 8b5bea967..000000000 Binary files a/webclient/src/images/faces/face2.jpg and /dev/null differ diff --git a/webclient/src/images/faces/face3.jpg b/webclient/src/images/faces/face3.jpg deleted file mode 100644 index ace651dc0..000000000 Binary files a/webclient/src/images/faces/face3.jpg and /dev/null differ diff --git a/webclient/src/images/index.ts b/webclient/src/images/index.ts deleted file mode 100644 index e96072916..000000000 --- a/webclient/src/images/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Images'; diff --git a/webclient/src/images/logo.png b/webclient/src/images/logo.png deleted file mode 100644 index 7ce83bd20..000000000 Binary files a/webclient/src/images/logo.png and /dev/null differ diff --git a/webclient/src/index.css b/webclient/src/index.css deleted file mode 100644 index c5f9b9230..000000000 --- a/webclient/src/index.css +++ /dev/null @@ -1,70 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Teko&display=swap'); -@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); - -:root { - -} - -* { - box-sizing: border-box; -} - -html, -body, -#root { - height: 100%; -} - -body { - margin: 0; - font-family: - -apple-system, - BlinkMacSystemFont, - "Open Sans", - "Segoe UI", - "Roboto", - "Oxygen", - "Ubuntu", - "Cantarell", - "Fira Sans", - "Droid Sans", - "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} - -strong, -b { - font-weight: bold; -} - -a { - color: inherit; - text-decoration: none; -} - -.overflow-scroll { - overflow-y: scroll; /* has to be scroll, not auto */ - -webkit-overflow-scrolling: touch; -} - -.plain-link { - color: inherit; - text-decoration: none; -} - -.single-line-ellipsis { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} - -.disabled-link { - pointer-events: none; -} diff --git a/webclient/src/index.tsx b/webclient/src/index.tsx deleted file mode 100644 index 97b5bb014..000000000 --- a/webclient/src/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { StrictMode } from 'react'; -import { createRoot } from 'react-dom/client'; -import { Theme, StyledEngineProvider } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; - -import { AppShell } from './containers'; -import { materialTheme } from './material-theme'; - -import './i18n'; -import './index.css'; - -const AppWithMaterialTheme = () => ( - - - - - - - -); - -const container = document.getElementById('root'); -const root = createRoot(container!); - -root.render(); diff --git a/webclient/src/material-theme.ts b/webclient/src/material-theme.ts deleted file mode 100644 index beee1146e..000000000 --- a/webclient/src/material-theme.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { PaletteMode } from '@mui/material'; -import { createTheme } from '@mui/material/styles'; - -const mode: PaletteMode = 'light'; - -const palette = { - mode, - - background: { - default: 'rgb(35, 35, 35)', - paper: '#FFFFFF', - }, - primary: { - main: '#7033DB', - light: 'rgba(112, 51, 219, .3)', - dark: '#401C7F', - contrastText: '#FFFFFF', - }, - grey: { - 50: '#fafafa', - 100: '#f5f5f5', - 200: '#eeeeee', - 300: '#e0e0e0', - 400: '#bdbdbd', - 500: '#9e9e9e', - 600: '#757575', - 700: '#616161', - 800: '#424242', - 900: '#212121', - A100: '#d5d5d5', - A200: '#aaaaaa', - A400: '#303030', - A700: '#616161', - }, - // secondary: { - // main: '', - // light: '', - // dark: '', - // contrastText: '', - // }, - // error: { - // main: '', - // light: '', - // dark: '', - // contrastText: '', - // }, - // warning: { - // main: '', - // light: '', - // dark: '', - // contrastText: '', - // }, - // info: { - // main: '', - // light: '', - // dark: '', - // contrastText: '', - // }, - success: { - main: '#6CDF39', - light: '#6CDF39', - // dark: '', - // contrastText: '', - }, -}; - -export const materialTheme = createTheme({ - palette, - - components: { - MuiCssBaseline: { - styleOverrides: { - '@global': { - '@font-face': [], - }, - }, - }, - MuiButton: { - styleOverrides: { - root: { - fontWeight: 'bold', - textTransform: 'none', - - '&.rounded': { - // 'border-radius': '50px', - }, - - '&.tall': { - 'height': '40px', - }, - }, - }, - }, - - MuiCheckbox: { - styleOverrides: { - root: { - '& .MuiSvgIcon-root': { - width: '.75em', - height: '.75em', - }, - }, - }, - }, - - MuiFormControlLabel: { - styleOverrides: { - label: { - fontSize: 12, - fontWeight: 'bold', - color: palette.primary.main, - }, - }, - }, - - MuiLink: { - styleOverrides: { - root: { - fontWeight: 'bold', - }, - }, - }, - - MuiList: { - styleOverrides: { - root: { - padding: '8px', - - '&.MuiList-padding': { - paddingBottom: '4px', - }, - - '& .MuiButton-root': { - width: '100%', - }, - - '& > .MuiButtonBase-root': { - padding: '8px 16px', - marginBottom: '4px', - borderRadius: 0, - justifyContent: 'space-between', - }, - - '& .MuiButtonBase-root.Mui-selected, & .MuiButtonBase-root.Mui-selected:focus': { - background: 'none', - fontWeight: 'bold', - }, - - ['& .MuiButtonBase-root:hover, & .MuiButtonBase-root.Mui-selected:hover']: { - background: palette.primary.light - }, - }, - }, - }, - - MuiListItem: { - styleOverrides: { - root: { - }, - } - }, - - MuiInputBase: { - styleOverrides: { - formControl: { - '& .MuiSelect-root svg': { - display: 'none', - }, - }, - }, - }, - - MuiOutlinedInput: { - styleOverrides: { - root: { - '&.Mui-focused .MuiOutlinedInput-notchedOutline': { - borderWidth: '1px', - }, - - '.rounded &': { - // 'border-radius': '50px', - }, - - '.tall &': { - height: '40px', - }, - }, - }, - }, - }, - - typography: { - fontSize: 12, - fontFamily: 'Open Sans, sans-serif', - - h1: { - fontSize: 28, - fontWeight: 'bold', - }, - h2: { - fontSize: 24, - fontWeight: 'bold', - }, - // h3: {}, - // h4: {}, - // h5: {}, - // h6: {}, - subtitle1: { - fontSize: 14, - fontWeight: 'bold', - lineHeight: 1.4, - color: palette.grey[500], - }, - subtitle2: { - lineHeight: 1.4, - color: palette.grey[500], - }, - body1: { - fontSize: '.75rem', - lineHeight: 1.4, - }, - // body2: {}, - // button: {}, - // caption: {}, - // overline: {}, - }, - - spacing: 8, - - breakpoints: { - values: { - xs: 0, - sm: 640, - md: 768, - lg: 1024, - xl: 1280, - }, - }, -}); diff --git a/webclient/src/react-app-env.d.ts b/webclient/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5fc..000000000 --- a/webclient/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/webclient/src/services/CardImporterService.ts b/webclient/src/services/CardImporterService.ts deleted file mode 100644 index 691e86f0e..000000000 --- a/webclient/src/services/CardImporterService.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Fetch and parse card sets - -class CardImporterService { - importCards(url): Promise { - const error = 'Card import must be in valid MTG JSON format'; - - return fetch(url) - .then(response => { - if (response.headers.get('Content-Type') !== 'application/json') { - throw new Error(error); - } - - return response.json(); - }) - .then((json) => { - try { - const sortedSets = Object.keys(json.data) - .map(key => json.data[key]) - .sort((a, b) => new Date(a.releaseDate).getTime() - new Date(b.releaseDate).getTime()); - - const sets = sortedSets.map(({ cards, tokens, ...set }) => ({ - ...set, - cards: cards.map(({ name }) => name), - tokens: tokens.map(({ name }) => name), - })); - - const unsortedCards = sortedSets.reduce((acc, set) => { - set.cards.forEach(card => acc[card.name] = card); - return acc; - }, {}); - - const cards = Object.keys(unsortedCards) - .sort((a, b) => a.localeCompare(b)) - .map(key => unsortedCards[key]); - - return { cards, sets }; - } catch (e) { - throw new Error(error); - } - }); - } - - importTokens(url): Promise { - const error = 'Token import must be in valid MTG XML format'; - - return fetch(url) - .then(response => { - if (!response.ok) { - throw new Error('Failed to fetch'); - } - - return response.text() - }) - .then((xmlString) => { - try { - const parser = new DOMParser(); - const dom = parser.parseFromString(xmlString, 'application/xml'); - - const tokens = Array.from(dom.querySelectorAll('card')).map( - (tokenElement) => this.parseXmlAttributes(tokenElement) - ); - - return tokens; - } catch (e) { - throw new Error(error); - } - }) - } - - private parseXmlAttributes(dom: Element) { - return Array.from(dom.children).reduce((attributes, child) => { - const value = child.children.length ? this.parseXmlAttributes(child) : child.innerHTML; - - let parsedAttributes = { value }; - - if (child.attributes.length) { - const childAttributes = Array.from(child.attributes).reduce((acc, { name, value }) => { - acc[name] = value; - return acc; - }, {}); - - parsedAttributes = { - ...parsedAttributes, - ...childAttributes, - }; - } - - // @TODO: clean this up and normalize what i'm returning - if (attributes[child.tagName]) { - if (Array.isArray(attributes[child.tagName])) { - attributes[child.tagName].push(parsedAttributes) - } else { - attributes[child.tagName] = [parsedAttributes]; - } - } else { - attributes[child.tagName] = parsedAttributes; - } - - return attributes; - }, {}); - } -} - -export const cardImporterService = new CardImporterService(); diff --git a/webclient/src/services/ServerProps.ts b/webclient/src/services/ServerProps.ts deleted file mode 100644 index 0d8c3d652..000000000 --- a/webclient/src/services/ServerProps.ts +++ /dev/null @@ -1,9 +0,0 @@ -import props from '../server-props.json'; - -class ServerProps { - get REACT_APP_VERSION() { - return props?.REACT_APP_VERSION; - } -} - -export const serverProps = new ServerProps(); diff --git a/webclient/src/services/dexie/DexieDTOs/CardDTO.ts b/webclient/src/services/dexie/DexieDTOs/CardDTO.ts deleted file mode 100644 index a060aedb1..000000000 --- a/webclient/src/services/dexie/DexieDTOs/CardDTO.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Card } from 'types'; - -import { dexieService } from '../DexieService'; - -export class CardDTO extends Card { - save() { - return dexieService.cards.put(this); - } - - static get(name) { - return dexieService.cards.where('name').equalsIgnoreCase(name).first(); - } - - static bulkAdd(cards: CardDTO[]): Promise { - return dexieService.cards.bulkPut(cards); - } -}; - -dexieService.cards.mapToClass(CardDTO); diff --git a/webclient/src/services/dexie/DexieDTOs/HostDTO.ts b/webclient/src/services/dexie/DexieDTOs/HostDTO.ts deleted file mode 100644 index 17f600626..000000000 --- a/webclient/src/services/dexie/DexieDTOs/HostDTO.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { IndexableType } from 'dexie'; -import { Host } from 'types'; - -import { dexieService } from '../DexieService'; - -export class HostDTO extends Host { - save() { - return dexieService.hosts.put(this); - } - - static add(host: Host): Promise { - return dexieService.hosts.add(host); - } - - static get(id: number): Promise { - return dexieService.hosts.where('id').equals(id).first(); - } - - static getAll(): Promise { - return dexieService.hosts.toArray(); - } - - static bulkAdd(hosts: Host[]): Promise { - return dexieService.hosts.bulkAdd(hosts); - } - - static delete(id: string): Promise { - return dexieService.hosts.delete(id); - } -}; - -dexieService.hosts.mapToClass(HostDTO); diff --git a/webclient/src/services/dexie/DexieDTOs/SetDTO.ts b/webclient/src/services/dexie/DexieDTOs/SetDTO.ts deleted file mode 100644 index 6ae2dd20a..000000000 --- a/webclient/src/services/dexie/DexieDTOs/SetDTO.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Set } from 'types'; - -import { dexieService } from '../DexieService'; - -export class SetDTO extends Set { - save() { - return dexieService.sets.put(this); - } - - static get(name) { - return dexieService.sets.where('name').equalsIgnoreCase(name).first(); - } - - static bulkAdd(sets: SetDTO[]): Promise { - return dexieService.sets.bulkPut(sets); - } -}; - -dexieService.sets.mapToClass(SetDTO); diff --git a/webclient/src/services/dexie/DexieDTOs/SettingDTO.ts b/webclient/src/services/dexie/DexieDTOs/SettingDTO.ts deleted file mode 100644 index bbfa1680d..000000000 --- a/webclient/src/services/dexie/DexieDTOs/SettingDTO.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Setting } from 'types'; - -import { dexieService } from '../DexieService'; - -export class SettingDTO extends Setting { - constructor(user) { - super(); - - this.user = user; - this.autoConnect = false; - } - - save() { - return dexieService.settings.put(this); - } - - static get(user) { - return dexieService.settings.where('user').equalsIgnoreCase(user).first(); - } -}; - -dexieService.settings.mapToClass(SettingDTO); diff --git a/webclient/src/services/dexie/DexieDTOs/TokenDTO.ts b/webclient/src/services/dexie/DexieDTOs/TokenDTO.ts deleted file mode 100644 index 35d537709..000000000 --- a/webclient/src/services/dexie/DexieDTOs/TokenDTO.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Token } from 'types'; - -import { dexieService } from '../DexieService'; - -export class TokenDTO extends Token { - save() { - return dexieService.tokens.put(this); - } - - static get(name) { - return dexieService.tokens.where('name.value').equalsIgnoreCase(name).first(); - } - - static bulkAdd(tokens: TokenDTO[]): Promise { - return dexieService.tokens.bulkPut(tokens); - } -}; - -dexieService.tokens.mapToClass(TokenDTO); diff --git a/webclient/src/services/dexie/DexieDTOs/index.ts b/webclient/src/services/dexie/DexieDTOs/index.ts deleted file mode 100644 index 9121f1f4b..000000000 --- a/webclient/src/services/dexie/DexieDTOs/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './CardDTO'; -export * from './SetDTO'; -export * from './SettingDTO'; -export * from './TokenDTO'; -export * from './HostDTO'; diff --git a/webclient/src/services/dexie/DexieSchemas/v1.schema.ts b/webclient/src/services/dexie/DexieSchemas/v1.schema.ts deleted file mode 100644 index 18e7f35c4..000000000 --- a/webclient/src/services/dexie/DexieSchemas/v1.schema.ts +++ /dev/null @@ -1,17 +0,0 @@ -export enum Stores { - SETTINGS = 'settings', - CARDS = 'cards', - SETS = 'sets', - TOKENS = 'tokens', - HOSTS = 'hosts', -} - -export const schemaV1 = (db) => { - db.version(1).stores({ - [Stores.CARDS]: 'name', - [Stores.SETS]: 'code', - [Stores.SETTINGS]: 'user', - [Stores.TOKENS]: 'name.value', - [Stores.HOSTS]: '++id', - }); -} diff --git a/webclient/src/services/dexie/DexieService.ts b/webclient/src/services/dexie/DexieService.ts deleted file mode 100644 index 9dcd3b941..000000000 --- a/webclient/src/services/dexie/DexieService.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Dexie from 'dexie'; - -import { Stores, schemaV1 } from './DexieSchemas/v1.schema'; - -class DexieService { - private db: Dexie = new Dexie('Webatrice'); - - constructor() { - schemaV1(this.db); - } - - get settings() { - return this.db.table(Stores.SETTINGS); - } - - get cards() { - return this.db.table(Stores.CARDS); - } - - get sets() { - return this.db.table(Stores.SETS); - } - - get tokens() { - return this.db.table(Stores.TOKENS); - } - - get hosts() { - return this.db.table(Stores.HOSTS); - } - - testConnection() { - return this.db.open(); - } -} - -export const dexieService = new DexieService(); diff --git a/webclient/src/services/dexie/index.ts b/webclient/src/services/dexie/index.ts deleted file mode 100644 index 593f19397..000000000 --- a/webclient/src/services/dexie/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './DexieDTOs'; -export * from './DexieService'; diff --git a/webclient/src/services/index.ts b/webclient/src/services/index.ts deleted file mode 100644 index ebebeb65e..000000000 --- a/webclient/src/services/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './CardImporterService'; -export * from './ServerProps'; -export * from './dexie'; diff --git a/webclient/src/setupTests.ts b/webclient/src/setupTests.ts deleted file mode 100644 index c4e40e2ec..000000000 --- a/webclient/src/setupTests.ts +++ /dev/null @@ -1,10 +0,0 @@ -import protobuf from 'protobufjs'; - -// ensure jest-dom is always available during testing to cut down on boilerplate -import '@testing-library/jest-dom'; - -class MockProtobufRoot { - load() {} -} - -(protobuf as any).Root = MockProtobufRoot; diff --git a/webclient/src/store/actions/actionReducer.ts b/webclient/src/store/actions/actionReducer.ts deleted file mode 100644 index a01dba984..000000000 --- a/webclient/src/store/actions/actionReducer.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @author Luke Brandon Farrell - * @description Application reducer. - */ - -import { AnyAction } from 'redux' - - interface InitialState { - type: string | null - payload: any - meta: any - error: boolean - count: number - } - -/** - * Initial data. - */ -const initialState: InitialState = { - type: null, - payload: null, - meta: null, - error: false, - count: 0, -} - -/** - * Calculates the application state. - * - * @param state - * @param action - * @return {*} - */ -export const actionReducer = ( - state = initialState, - action: AnyAction, -): InitialState => { - return { - ...state, - ...action, - count: state.count + 1, - } -} diff --git a/webclient/src/store/actions/index.ts b/webclient/src/store/actions/index.ts deleted file mode 100644 index 4c5f9ef48..000000000 --- a/webclient/src/store/actions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { actionReducer } from './actionReducer'; diff --git a/webclient/src/store/common/SortUtil.ts b/webclient/src/store/common/SortUtil.ts deleted file mode 100644 index 56910259d..000000000 --- a/webclient/src/store/common/SortUtil.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { SortBy, SortDirection, User } from 'types'; - -export default class SortUtil { - static sortByField(arr: any[], sortBy: SortBy): void { - if (arr.length) { - const field = SortUtil.resolveFieldChain(arr[0], sortBy.field); - const fieldType = typeof field; - - if (fieldType === 'string') { - SortUtil.sortByString(arr, sortBy); - return; - } - - if (fieldType === 'number') { - SortUtil.sortByNumber(arr, sortBy); - return; - } - - throw new Error('SortField must resolve to either a string or number'); - } - } - - static sortByFields(arr: any[], sorts: SortBy[]) { - if (arr.length) { - arr.sort((a, b) => { - for (let i = 0; i < sorts.length; i++) { - const sortBy = sorts[i]; - const field = SortUtil.resolveFieldChain(arr[0], sortBy.field); - - const fieldType = typeof field; - - if (fieldType === 'string') { - const result = SortUtil.stringComparator(a, b, sortBy); - - if (result) { - return result; - } - } - - if (fieldType === 'number') { - const result = SortUtil.numberComparator(a, b, sortBy); - - if (result) { - return result; - } - } - - throw new Error('SortField must resolve to either a string or number'); - } - - return 0; - }) - } - } - - static sortUsersByField(users: User[], sortBy: SortBy) { - if (users.length) { - users.sort((a, b) => SortUtil.userComparator(a, b, sortBy)) - } - } - - static toggleSortBy(field: string, sortBy: SortBy) { - const sameField = field === sortBy.field; - const isASC = sortBy.order === SortDirection.ASC; - - return { - field, - order: sameField && isASC ? SortDirection.DESC : SortDirection.ASC - } - } - - private static sortByNumber(arr: any[], sortBy: SortBy): void { - arr.sort((a, b) => SortUtil.numberComparator(a, b, sortBy)); - } - - private static sortByString(arr: any[], sortBy: SortBy): void { - arr.sort((a, b) => SortUtil.stringComparator(a, b, sortBy)); - } - - private static userComparator(a, b, sortBy, sortByUserLevel = true) { - if (sortByUserLevel) { - const adminSortBy = { - field: 'userLevel', - order: SortDirection.DESC - }; - - const adminSorted = SortUtil.numberComparator(a, b, adminSortBy); - - if (adminSorted) { - return adminSorted; - } - } - - const sorted = SortUtil.stringComparator(a, b, sortBy); - - if (sorted) { - return sorted; - } - - return 0; - } - - private static numberComparator(a, b, { field, order }: SortBy) { - const aResolved = SortUtil.resolveFieldChain(a, field); - const bResolved = SortUtil.resolveFieldChain(b, field); - - if (order === SortDirection.ASC) { - return aResolved - bResolved; - } else { - return bResolved - aResolved; - } - } - - private static stringComparator(a, b, { field, order }: SortBy) { - const aResolved = SortUtil.resolveFieldChain(a, field); - const bResolved = SortUtil.resolveFieldChain(b, field); - - // Force empty strings to sort to bottom - if (!aResolved && !bResolved) { - return 0; - } - if (!aResolved) { - return 1; - } - if (!bResolved) { - return -1; - } - - if (order === SortDirection.ASC) { - return aResolved.localeCompare(bResolved); - } else { - return bResolved.localeCompare(aResolved); - } - } - - private static resolveFieldChain(obj: object, field: string) { - const links = field.split('.'); - - if (links.length > 1) { - return links.reduce((obj, link) => { - const parsed = parseInt(link, 10); - - if (parsed.toLocaleString() === 'NaN') { - return obj[link]; - } else { - return obj[parsed]; - } - }, obj) || null; - } else { - return obj[field]; - } - } -} diff --git a/webclient/src/store/common/index.ts b/webclient/src/store/common/index.ts deleted file mode 100644 index 340676b6d..000000000 --- a/webclient/src/store/common/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as SortUtil } from './SortUtil'; diff --git a/webclient/src/store/index.ts b/webclient/src/store/index.ts deleted file mode 100644 index b43f290c5..000000000 --- a/webclient/src/store/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { store } from './store'; - -// Common -export { SortUtil } from './common'; - -// Server - -export { - Types as ServerTypes, - Selectors as ServerSelectors, - Dispatch as ServerDispatch } from './server'; - -export * from 'store/server/server.interfaces'; - -export { - Types as RoomsTypes, - Selectors as RoomsSelectors, - Dispatch as RoomsDispatch } from 'store/rooms'; - -export * from 'store/rooms/rooms.interfaces'; - - diff --git a/webclient/src/store/rooms/index.ts b/webclient/src/store/rooms/index.ts deleted file mode 100644 index 3ebe3905a..000000000 --- a/webclient/src/store/rooms/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './rooms.actions'; -export * from './rooms.dispatch'; -export * from './rooms.reducer'; -export * from './rooms.selectors'; -export * from './rooms.types'; diff --git a/webclient/src/store/rooms/rooms.actions.tsx b/webclient/src/store/rooms/rooms.actions.tsx deleted file mode 100644 index 98108fe8b..000000000 --- a/webclient/src/store/rooms/rooms.actions.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Types } from './rooms.types'; - -export const Actions = { - clearStore: () => ({ - type: Types.CLEAR_STORE - }), - - updateRooms: rooms => ({ - type: Types.UPDATE_ROOMS, - rooms - }), - - joinRoom: roomInfo => ({ - type: Types.JOIN_ROOM, - roomInfo - }), - - leaveRoom: roomId => ({ - type: Types.LEAVE_ROOM, - roomId - }), - - addMessage: (roomId, message) => ({ - type: Types.ADD_MESSAGE, - roomId, - message - }), - - updateGames: (roomId, games) => ({ - type: Types.UPDATE_GAMES, - roomId, - games - }), - - userJoined: (roomId, user) => ({ - type: Types.USER_JOINED, - roomId, - user - }), - - userLeft: (roomId, name) => ({ - type: Types.USER_LEFT, - roomId, - name - }), - - sortGames: (roomId, field, order) => ({ - type: Types.SORT_GAMES, - roomId, - field, - order - }), - - removeMessages: (roomId, name, amount) => ({ - type: Types.REMOVE_MESSAGES, - roomId, - name, - amount - }), - - gameCreated: (roomId) => ({ - type: Types.GAME_CREATED, - roomId - }), - - joinedGame: (roomId, gameId) => ({ - type: Types.JOINED_GAME, - roomId, - gameId - }), -} diff --git a/webclient/src/store/rooms/rooms.dispatch.tsx b/webclient/src/store/rooms/rooms.dispatch.tsx deleted file mode 100644 index c89b5ebc4..000000000 --- a/webclient/src/store/rooms/rooms.dispatch.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { reset } from 'redux-form'; -import { Actions } from './rooms.actions'; -import { store } from 'store'; - -export const Dispatch = { - clearStore: () => { - store.dispatch(Actions.clearStore()); - }, - - updateRooms: rooms => { - store.dispatch(Actions.updateRooms(rooms)); - }, - - joinRoom: roomInfo => { - store.dispatch(Actions.joinRoom(roomInfo)); - - }, - - leaveRoom: roomId => { - store.dispatch(Actions.leaveRoom(roomId)); - }, - - addMessage: (roomId, message) => { - if (message.name) { - store.dispatch(reset('sayMessage')); - } - - store.dispatch(Actions.addMessage(roomId, message)); - }, - - updateGames: (roomId, games) => { - store.dispatch(Actions.updateGames(roomId, games)); - }, - - userJoined: (roomId, user) => { - store.dispatch(Actions.userJoined(roomId, user)); - }, - - userLeft: (roomId, name) => { - store.dispatch(Actions.userLeft(roomId, name)); - }, - - sortGames: (roomId, field, order) => { - store.dispatch(Actions.sortGames(roomId, field, order)); - }, - - removeMessages: (roomId, name, amount) => { - store.dispatch(Actions.removeMessages(roomId, name, amount)); - }, - - gameCreated: (roomId) => { - store.dispatch(Actions.gameCreated(roomId)); - }, - - joinedGame: (roomId, gameId) => { - store.dispatch(Actions.joinedGame(roomId, gameId)); - } -} diff --git a/webclient/src/store/rooms/rooms.interfaces.tsx b/webclient/src/store/rooms/rooms.interfaces.tsx deleted file mode 100644 index c7b90ac84..000000000 --- a/webclient/src/store/rooms/rooms.interfaces.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { GameSortField, Room, Game, SortBy, UserSortField } from 'types'; - -export interface RoomsState { - rooms: RoomsStateRooms; - games: RoomsStateGames; - joinedRoomIds: JoinedRooms; - joinedGameIds: JoinedGames; - messages: RoomsStateMessages; - sortGamesBy: RoomsStateSortGamesBy; - sortUsersBy: RoomsStateSortUsersBy; -} - -export interface RoomsStateRooms { - [roomId: number]: Room; -} - -export interface RoomsStateGames { - [roomId: number]: { - [gameId: number]: Game; - }; -} - -export interface JoinedRooms { - [roomId: number]: boolean; -} - -export interface JoinedGames { - [roomId: number]: { - [gameId: number]: boolean; - }; -} - -export interface RoomsStateMessages { - [roomId: number]: Message[]; -} - -export interface RoomsStateSortGamesBy extends SortBy { - field: GameSortField -} - -export interface RoomsStateSortUsersBy extends SortBy { - field: UserSortField -} - -export interface Message { - message: string; - messageType: number; - timeReceived: number; - timeOf?: number; -} diff --git a/webclient/src/store/rooms/rooms.reducer.tsx b/webclient/src/store/rooms/rooms.reducer.tsx deleted file mode 100644 index 5898c7150..000000000 --- a/webclient/src/store/rooms/rooms.reducer.tsx +++ /dev/null @@ -1,326 +0,0 @@ -import * as _ from 'lodash'; - -import { GameSortField, UserSortField, SortDirection } from 'types'; - -import { SortUtil } from '../common'; - -import { RoomsState } from './rooms.interfaces' -import { MAX_ROOM_MESSAGES, Types } from './rooms.types'; - -const initialState: RoomsState = { - rooms: {}, - games: {}, - joinedRoomIds: {}, - joinedGameIds: {}, - messages: {}, - sortGamesBy: { - field: GameSortField.START_TIME, - order: SortDirection.DESC - }, - sortUsersBy: { - field: UserSortField.NAME, - order: SortDirection.ASC - } -}; - -export const roomsReducer = (state = initialState, action: any) => { - switch (action.type) { - case Types.CLEAR_STORE: { - return { - ...initialState - }; - } - - case Types.UPDATE_ROOMS: { - const rooms = { - ...state.rooms - }; - - // Server does not send everything on updates - _.each(action.rooms, (room, order) => { - const { roomId } = room; - const existing = rooms[roomId] || {}; - - const update = { ...room }; - delete update.gameList; - delete update.gametypeList; - delete update.userList; - - rooms[roomId] = { - ...existing, - ...update, - order - }; - }); - - return { ...state, rooms }; - } - - case Types.JOIN_ROOM: { - const { roomInfo } = action; - const { joinedRoomIds, rooms, sortGamesBy, sortUsersBy } = state; - - const { roomId } = roomInfo; - - const gameList = [ - ...roomInfo.gameList - ]; - - const userList = [ - ...roomInfo.userList - ]; - - SortUtil.sortByField(gameList, sortGamesBy); - SortUtil.sortUsersByField(userList, sortUsersBy); - - return { - ...state, - - rooms: { - ...rooms, - [roomId]: { - ...roomInfo, - gameList, - userList - } - }, - - joinedRoomIds: { - ...joinedRoomIds, - [roomId]: true - }, - } - } - - case Types.LEAVE_ROOM: { - const { roomId } = action; - const { joinedRoomIds, messages } = state; - - const _joined = { - ...joinedRoomIds - }; - - const _messages = { - ...messages - }; - - delete _joined[roomId]; - delete _messages[roomId]; - - return { - ...state, - - joinedRoomIds: _joined, - messages: _messages, - } - } - - case Types.ADD_MESSAGE: { - const { roomId, message } = action; - const { messages } = state; - - let roomMessages = [...(messages[roomId] || [])]; - - if (roomMessages.length === MAX_ROOM_MESSAGES) { - roomMessages.shift(); - } - - message.timeReceived = new Date().getTime(); - roomMessages.push(message); - - return { - ...state, - messages: { - ...messages, - - [roomId]: [ - ...roomMessages - ] - } - } - } - // @TODO improve this reducer, likely by improving the store model - - case Types.UPDATE_GAMES: { - const { roomId, games } = action; - const { rooms, sortGamesBy } = state; - const room = rooms[roomId]; - - if (!room) { - return { ...state }; - } - - // Create map of games with update objects - const toUpdate = games.reduce((map, game) => { - map[game.gameId] = game; - return map; - }, {}); - - const gameUpdates = room.gameList - // filter out closed games and remove from update map - .filter(game => { - const gameUpdate = toUpdate[game.gameId]; - const closedGame = gameUpdate && gameUpdate.closed; - - if (closedGame) { - delete toUpdate[game.gameId]; - } - - return !closedGame; - }) - .map(game => { - const gameUpdate = toUpdate[game.gameId]; - - if (gameUpdate) { - delete toUpdate[game.gameId]; - - return { - ...game, - ...gameUpdate - }; - } - - return game; - }); - - // Push new games to end of list - if (_.size(toUpdate)) { - _.each(toUpdate, game => gameUpdates.push(game)); - } - - const gameList = [...gameUpdates]; - - SortUtil.sortByField(gameList, sortGamesBy); - - return { - ...state, - rooms: { - ...rooms, - [roomId]: { - ...room, - gameList - } - } - } - } - - case Types.USER_JOINED: { - const { roomId, user } = action; - const { rooms, sortUsersBy } = state; - - const room = { ...rooms[roomId] }; - - const userList = [ - ...room.userList, - user - ]; - - SortUtil.sortUsersByField(userList, sortUsersBy); - - return { - ...state, - rooms: { - ...rooms, - [roomId]: { - ...room, - userList - } - } - }; - } - - case Types.USER_LEFT: { - const { roomId, name } = action; - const { rooms } = state; - - const room = { ...rooms[roomId] }; - const userList = room.userList.filter(user => user.name !== name); - - return { - ...state, - rooms: { - ...rooms, - [roomId]: { - ...room, - userList - } - } - }; - } - - case Types.SORT_GAMES: { - const { field, order, roomId } = action; - const { rooms } = state; - - const gameList = [...rooms[roomId].gameList]; - - const sortGamesBy = { - field, order - }; - - SortUtil.sortByField(gameList, sortGamesBy); - - return { - ...state, - - rooms: { - ...rooms, - [roomId]: { - ...rooms[roomId], - gameList - } - }, - - sortGamesBy - } - } - - case Types.REMOVE_MESSAGES: { - const { name, amount, roomId } = action; - const { messages } = state; - let amountRemoved = 0; - - return { - ...state, - messages: { - ...messages, - [roomId]: messages[roomId] - .reverse() - .filter(({ message }) => { - if (amount === amountRemoved) { - return true; - } - - const keep = message.indexOf(`${name}:`) !== 0; - - if (!keep) { - amountRemoved++; - } - - return keep; - }) - .reverse() - } - } - } - - case Types.JOINED_GAME: { - const { gameId, roomId } = action; - const { joinedGameIds } = state; - - return { - ...state, - joinedGameIds: { - ...joinedGameIds, - [roomId]: { - ...joinedGameIds[roomId], - [gameId]: true, - } - } - } - } - - default: - return state; - } -} diff --git a/webclient/src/store/rooms/rooms.selectors.tsx b/webclient/src/store/rooms/rooms.selectors.tsx deleted file mode 100644 index e1e7ec818..000000000 --- a/webclient/src/store/rooms/rooms.selectors.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import * as _ from 'lodash'; -import { RoomsState } from './rooms.interfaces'; - -interface State { - rooms: RoomsState -} - -export const Selectors = { - getRooms: ({ rooms }: State) => rooms.rooms, - getGames: ({ rooms }: State) => rooms.games, - getRoom: ({ rooms }: State, id: number) => - _.find(rooms.rooms, ({ roomId }) => roomId === id), - getJoinedRoomIds: ({ rooms }: State) => rooms.joinedRoomIds, - getJoinedGameIds: ({ rooms }: State) => rooms.joinedGameIds, - getMessages: ({ rooms }: State) => rooms.messages, - getSortGamesBy: ({ rooms: { sortGamesBy } }: State) => sortGamesBy, - getSortUsersBy: ({ rooms: { sortUsersBy } }: State) => sortUsersBy, - - getJoinedRooms: (state: State) => { - const joined = Selectors.getJoinedRoomIds(state); - return _.filter(Selectors.getRooms(state), room => joined[room.roomId]); - }, - - getJoinedGames: (state: State, roomId: number) => { - const joined = Selectors.getJoinedGameIds(state)[roomId]; - return _.filter(Selectors.getGames(state)[roomId], game => joined[game.gameId]); - }, - - getRoomMessages: (state: State, roomId: number) => Selectors.getMessages(state)[roomId], - getRoomGames: (state: State, roomId: number) => Selectors.getRooms(state)[roomId].gameList, - getRoomUsers: (state: State, roomId: number) => Selectors.getRooms(state)[roomId].userList - -} - diff --git a/webclient/src/store/rooms/rooms.types.tsx b/webclient/src/store/rooms/rooms.types.tsx deleted file mode 100644 index 0b07eadd1..000000000 --- a/webclient/src/store/rooms/rooms.types.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export const Types = { - CLEAR_STORE: '[Rooms] Clear Store', - UPDATE_ROOMS: '[Rooms] Update Rooms', - JOIN_ROOM: '[Rooms] Join Room', - LEAVE_ROOM: '[Rooms] Leave Room', - ADD_MESSAGE: '[Rooms] Add Message', - UPDATE_GAMES: '[Rooms] Update Games', - USER_JOINED: '[Rooms] User Joined', - USER_LEFT: '[Rooms] User Left', - SORT_GAMES: '[Rooms] Sort Games', - REMOVE_MESSAGES: '[Rooms] Remove Messages', - GAME_CREATED: '[Rooms] Game Created', - JOINED_GAME: '[Rooms] Joined Game', -}; - -export const MAX_ROOM_MESSAGES = 1000; diff --git a/webclient/src/store/rootReducer.ts b/webclient/src/store/rootReducer.ts deleted file mode 100644 index 0f39d7e37..000000000 --- a/webclient/src/store/rootReducer.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { combineReducers } from 'redux'; - -import { roomsReducer } from './rooms'; -import { serverReducer } from './server'; -import { reducer as formReducer } from 'redux-form' -import { actionReducer } from './actions' - -export default combineReducers({ - rooms: roomsReducer, - server: serverReducer, - - form: formReducer, - action: actionReducer -}); diff --git a/webclient/src/store/server/index.ts b/webclient/src/store/server/index.ts deleted file mode 100644 index f2a9c5dca..000000000 --- a/webclient/src/store/server/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { Actions } from './server.actions'; -export { Dispatch } from './server.dispatch'; -export * from './server.reducer'; -export { Selectors } from './server.selectors'; -export * from './server.types'; diff --git a/webclient/src/store/server/server.actions.ts b/webclient/src/store/server/server.actions.ts deleted file mode 100644 index 8af5adb75..000000000 --- a/webclient/src/store/server/server.actions.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { DeckList, DeckStorageTreeItem, ReplayMatch, WebSocketConnectOptions } from 'types'; -import { Types } from './server.types'; - -export const Actions = { - initialized: () => ({ - type: Types.INITIALIZED - }), - clearStore: () => ({ - type: Types.CLEAR_STORE - }), - loginSuccessful: (options: WebSocketConnectOptions) => ({ - type: Types.LOGIN_SUCCESSFUL, - options - }), - loginFailed: () => ({ - type: Types.LOGIN_FAILED, - }), - connectionClosed: reason => ({ - type: Types.CONNECTION_CLOSED, - reason - }), - connectionFailed: () => ({ - type: Types.CONNECTION_FAILED, - }), - testConnectionSuccessful: () => ({ - type: Types.TEST_CONNECTION_SUCCESSFUL, - }), - testConnectionFailed: () => ({ - type: Types.TEST_CONNECTION_FAILED, - }), - serverMessage: message => ({ - type: Types.SERVER_MESSAGE, - message - }), - updateBuddyList: buddyList => ({ - type: Types.UPDATE_BUDDY_LIST, - buddyList - }), - addToBuddyList: user => ({ - type: Types.ADD_TO_BUDDY_LIST, - user - }), - removeFromBuddyList: userName => ({ - type: Types.REMOVE_FROM_BUDDY_LIST, - userName - }), - updateIgnoreList: ignoreList => ({ - type: Types.UPDATE_IGNORE_LIST, - ignoreList - }), - addToIgnoreList: user => ({ - type: Types.ADD_TO_IGNORE_LIST, - user - }), - removeFromIgnoreList: userName => ({ - type: Types.REMOVE_FROM_IGNORE_LIST, - userName - }), - updateInfo: info => ({ - type: Types.UPDATE_INFO, - info - }), - updateStatus: status => ({ - type: Types.UPDATE_STATUS, - status - }), - updateUser: user => ({ - type: Types.UPDATE_USER, - user - }), - updateUsers: users => ({ - type: Types.UPDATE_USERS, - users - }), - userJoined: user => ({ - type: Types.USER_JOINED, - user - }), - userLeft: name => ({ - type: Types.USER_LEFT, - name - }), - viewLogs: logs => ({ - type: Types.VIEW_LOGS, - logs - }), - clearLogs: () => ({ - type: Types.CLEAR_LOGS, - }), - registrationRequiresEmail: () => ({ - type: Types.REGISTRATION_REQUIRES_EMAIL, - }), - registrationSuccess: () => ({ - type: Types.REGISTRATION_SUCCES, - }), - registrationFailed: (error) => ({ - type: Types.REGISTRATION_FAILED, - error - }), - registrationEmailError: (error) => ({ - type: Types.REGISTRATION_EMAIL_ERROR, - error - }), - registrationPasswordError: (error) => ({ - type: Types.REGISTRATION_PASSWORD_ERROR, - error - }), - registrationUserNameError: (error) => ({ - type: Types.REGISTRATION_USERNAME_ERROR, - error - }), - accountAwaitingActivation: (options: WebSocketConnectOptions) => ({ - type: Types.ACCOUNT_AWAITING_ACTIVATION, - options - }), - accountActivationSuccess: () => ({ - type: Types.ACCOUNT_ACTIVATION_SUCCESS, - }), - accountActivationFailed: () => ({ - type: Types.ACCOUNT_ACTIVATION_FAILED, - }), - resetPassword: () => ({ - type: Types.RESET_PASSWORD_REQUESTED, - }), - resetPasswordFailed: () => ({ - type: Types.RESET_PASSWORD_FAILED, - }), - resetPasswordChallenge: () => ({ - type: Types.RESET_PASSWORD_CHALLENGE, - }), - resetPasswordSuccess: () => ({ - type: Types.RESET_PASSWORD_SUCCESS, - }), - adjustMod: (userName, shouldBeMod, shouldBeJudge) => ({ - type: Types.ADJUST_MOD, - userName, - shouldBeMod, - shouldBeJudge, - }), - reloadConfig: () => ({ - type: Types.RELOAD_CONFIG, - }), - shutdownServer: () => ({ - type: Types.SHUTDOWN_SERVER, - }), - updateServerMessage: () => ({ - type: Types.UPDATE_SERVER_MESSAGE, - }), - accountPasswordChange: () => ({ - type: Types.ACCOUNT_PASSWORD_CHANGE, - }), - accountEditChanged: (user) => ({ - type: Types.ACCOUNT_EDIT_CHANGED, - user, - }), - accountImageChanged: (user) => ({ - type: Types.ACCOUNT_IMAGE_CHANGED, - user, - }), - directMessageSent: (userName, message) => ({ - type: Types.DIRECT_MESSAGE_SENT, - userName, - message, - }), - getUserInfo: (userInfo) => ({ - type: Types.GET_USER_INFO, - userInfo, - }), - notifyUser: (notification) => ({ - type: Types.NOTIFY_USER, - notification, - }), - serverShutdown: (data) => ({ - type: Types.SERVER_SHUTDOWN, - data, - }), - userMessage: (messageData) => ({ - type: Types.USER_MESSAGE, - messageData, - }), - addToList: (list, userName) => ({ - type: Types.ADD_TO_LIST, - list, - userName, - }), - removeFromList: (list, userName) => ({ - type: Types.REMOVE_FROM_LIST, - list, - userName, - }), - banFromServer: (userName) => ({ - type: Types.BAN_FROM_SERVER, - userName, - }), - banHistory: (userName, banHistory) => ({ - type: Types.BAN_HISTORY, - userName, - banHistory, - }), - warnHistory: (userName, warnHistory) => ({ - type: Types.WARN_HISTORY, - userName, - warnHistory, - }), - warnListOptions: (warnList) => ({ - type: Types.WARN_LIST_OPTIONS, - warnList, - }), - warnUser: (userName) => ({ - type: Types.WARN_USER, - userName, - }), - grantReplayAccess: (replayId: number, moderatorName: string) => ({ - type: Types.GRANT_REPLAY_ACCESS, - replayId, - moderatorName, - }), - forceActivateUser: (usernameToActivate: string, moderatorName: string) => ({ - type: Types.FORCE_ACTIVATE_USER, - usernameToActivate, - moderatorName, - }), - getAdminNotes: (userName: string, notes: string) => ({ - type: Types.GET_ADMIN_NOTES, - userName, - notes, - }), - updateAdminNotes: (userName: string, notes: string) => ({ - type: Types.UPDATE_ADMIN_NOTES, - userName, - notes, - }), - replayList: (matchList: ReplayMatch[]) => ({ type: Types.REPLAY_LIST, matchList }), - replayAdded: (matchInfo: ReplayMatch) => ({ type: Types.REPLAY_ADDED, matchInfo }), - replayModifyMatch: (gameId: number, doNotHide: boolean) => ({ type: Types.REPLAY_MODIFY_MATCH, gameId, doNotHide }), - replayDeleteMatch: (gameId: number) => ({ type: Types.REPLAY_DELETE_MATCH, gameId }), - backendDecks: (deckList: DeckList) => ({ type: Types.BACKEND_DECKS, deckList }), - deckNewDir: (path: string, dirName: string) => ({ type: Types.DECK_NEW_DIR, path, dirName }), - deckDelDir: (path: string) => ({ type: Types.DECK_DEL_DIR, path }), - deckUpload: (path: string, treeItem: DeckStorageTreeItem) => ({ type: Types.DECK_UPLOAD, path, treeItem }), - deckDelete: (deckId: number) => ({ type: Types.DECK_DELETE, deckId }), -} diff --git a/webclient/src/store/server/server.dispatch.ts b/webclient/src/store/server/server.dispatch.ts deleted file mode 100644 index 3d3b6d360..000000000 --- a/webclient/src/store/server/server.dispatch.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { reset } from 'redux-form'; -import { Actions } from './server.actions'; -import { store } from 'store'; -import { DeckList, DeckStorageTreeItem, ReplayMatch, WebSocketConnectOptions } from 'types'; - -export const Dispatch = { - initialized: () => { - store.dispatch(Actions.initialized()); - }, - clearStore: () => { - store.dispatch(Actions.clearStore()); - }, - loginSuccessful: options => { - store.dispatch(Actions.loginSuccessful(options)); - }, - loginFailed: () => { - store.dispatch(Actions.loginFailed()); - }, - connectionClosed: reason => { - store.dispatch(Actions.connectionClosed(reason)); - }, - connectionFailed: () => { - store.dispatch(Actions.connectionFailed()); - }, - testConnectionSuccessful: () => { - store.dispatch(Actions.testConnectionSuccessful()); - }, - testConnectionFailed: () => { - store.dispatch(Actions.testConnectionFailed()); - }, - updateBuddyList: buddyList => { - store.dispatch(Actions.updateBuddyList(buddyList)); - }, - addToBuddyList: user => { - store.dispatch(reset('addToBuddies')); - store.dispatch(Actions.addToBuddyList(user)); - }, - removeFromBuddyList: userName => { - store.dispatch(Actions.removeFromBuddyList(userName)); - }, - updateIgnoreList: ignoreList => { - store.dispatch(Actions.updateIgnoreList(ignoreList)); - }, - addToIgnoreList: user => { - store.dispatch(reset('addToIgnore')); - store.dispatch(Actions.addToIgnoreList(user)); - }, - removeFromIgnoreList: userName => { - store.dispatch(Actions.removeFromIgnoreList(userName)); - }, - updateInfo: (name, version) => { - store.dispatch(Actions.updateInfo({ - name, - version - })); - }, - updateStatus: (state, description) => { - store.dispatch(Actions.updateStatus({ - state, - description - })); - }, - updateUser: user => { - store.dispatch(Actions.updateUser(user)); - }, - updateUsers: users => { - store.dispatch(Actions.updateUsers(users)); - }, - userJoined: user => { - store.dispatch(Actions.userJoined(user)); - }, - userLeft: name => { - store.dispatch(Actions.userLeft(name)); - }, - viewLogs: name => { - store.dispatch(Actions.viewLogs(name)); - }, - clearLogs: () => { - store.dispatch(Actions.clearLogs()); - }, - serverMessage: message => { - store.dispatch(Actions.serverMessage(message)); - }, - registrationRequiresEmail: () => { - store.dispatch(Actions.registrationRequiresEmail()); - }, - registrationSuccess: () => { - store.dispatch(Actions.registrationSuccess()) - }, - registrationFailed: (error) => { - store.dispatch(Actions.registrationFailed(error)); - }, - registrationEmailError: (error) => { - store.dispatch(Actions.registrationEmailError(error)); - }, - registrationPasswordError: (error) => { - store.dispatch(Actions.registrationPasswordError(error)); - }, - registrationUserNameError: (error) => { - store.dispatch(Actions.registrationUserNameError(error)); - }, - accountAwaitingActivation: (options: WebSocketConnectOptions) => { - store.dispatch(Actions.accountAwaitingActivation(options)); - }, - accountActivationSuccess: () => { - store.dispatch(Actions.accountActivationSuccess()); - }, - accountActivationFailed: () => { - store.dispatch(Actions.accountActivationFailed()); - }, - resetPassword: () => { - store.dispatch(Actions.resetPassword()); - }, - resetPasswordFailed: () => { - store.dispatch(Actions.resetPasswordFailed()); - }, - resetPasswordChallenge: () => { - store.dispatch(Actions.resetPasswordChallenge()); - }, - resetPasswordSuccess: () => { - store.dispatch(Actions.resetPasswordSuccess()); - }, - adjustMod: (userName, shouldBeMod, shouldBeJudge) => { - store.dispatch(Actions.adjustMod(userName, shouldBeMod, shouldBeJudge)); - }, - reloadConfig: () => { - store.dispatch(Actions.reloadConfig()); - }, - shutdownServer: () => { - store.dispatch(Actions.shutdownServer()); - }, - updateServerMessage: () => { - store.dispatch(Actions.updateServerMessage()); - }, - accountPasswordChange: () => { - store.dispatch(Actions.accountPasswordChange()); - }, - accountEditChanged: (user) => { - store.dispatch(Actions.accountEditChanged(user)); - }, - accountImageChanged: (user) => { - store.dispatch(Actions.accountImageChanged(user)); - }, - directMessageSent: (userName, message) => { - store.dispatch(Actions.directMessageSent(userName, message)); - }, - getUserInfo: (userInfo) => { - store.dispatch(Actions.getUserInfo(userInfo)); - }, - notifyUser: (notification) => { - store.dispatch(Actions.notifyUser(notification)) - }, - serverShutdown: (data) => { - store.dispatch(Actions.serverShutdown(data)) - }, - userMessage: (messageData) => { - store.dispatch(Actions.userMessage(messageData)) - }, - addToList: (list, userName) => { - store.dispatch(Actions.addToList(list, userName)) - }, - removeFromList: (list, userName) => { - store.dispatch(Actions.removeFromList(list, userName)) - }, - banFromServer: (userName) => { - store.dispatch(Actions.banFromServer(userName)); - }, - banHistory: (userName, banHistory) => { - store.dispatch(Actions.banHistory(userName, banHistory)) - }, - warnHistory: (userName, warnHistory) => { - store.dispatch(Actions.warnHistory(userName, warnHistory)) - }, - warnListOptions: (warnList) => { - store.dispatch(Actions.warnListOptions(warnList)) - }, - warnUser: (userName) => { - store.dispatch(Actions.warnUser(userName)) - }, - grantReplayAccess: (replayId: number, moderatorName: string) => { - store.dispatch(Actions.grantReplayAccess(replayId, moderatorName)); - }, - forceActivateUser: (usernameToActivate: string, moderatorName: string) => { - store.dispatch(Actions.forceActivateUser(usernameToActivate, moderatorName)); - }, - getAdminNotes: (userName: string, notes: string) => { - store.dispatch(Actions.getAdminNotes(userName, notes)); - }, - updateAdminNotes: (userName: string, notes: string) => { - store.dispatch(Actions.updateAdminNotes(userName, notes)); - }, - replayList: (matchList: ReplayMatch[]) => { - store.dispatch(Actions.replayList(matchList)); - }, - replayAdded: (matchInfo: ReplayMatch) => { - store.dispatch(Actions.replayAdded(matchInfo)); - }, - replayModifyMatch: (gameId: number, doNotHide: boolean) => { - store.dispatch(Actions.replayModifyMatch(gameId, doNotHide)); - }, - replayDeleteMatch: (gameId: number) => { - store.dispatch(Actions.replayDeleteMatch(gameId)); - }, - backendDecks: (deckList: DeckList) => { - store.dispatch(Actions.backendDecks(deckList)); - }, - deckNewDir: (path: string, dirName: string) => { - store.dispatch(Actions.deckNewDir(path, dirName)); - }, - deckDelDir: (path: string) => { - store.dispatch(Actions.deckDelDir(path)); - }, - deckUpload: (path: string, treeItem: DeckStorageTreeItem) => { - store.dispatch(Actions.deckUpload(path, treeItem)); - }, - deckDelete: (deckId: number) => { - store.dispatch(Actions.deckDelete(deckId)); - }, -} diff --git a/webclient/src/store/server/server.interfaces.ts b/webclient/src/store/server/server.interfaces.ts deleted file mode 100644 index 97e9d99da..000000000 --- a/webclient/src/store/server/server.interfaces.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - WarnHistoryItem, BanHistoryItem, DeckList, LogItem, ReplayMatch, SortBy, User, UserSortField, WebSocketConnectOptions, WarnListItem -} from 'types'; -import { NotifyUserData, ServerShutdownData, UserMessageData } from 'websocket/events/session/interfaces'; - -export interface ServerConnectParams { - host: string; - port: string; - userName: string; - password: string; -} - -export interface ServerRegisterParams { - host: string; - port: string; - userName: string; - password: string; - email: string; - country: string; - realName: string; -} - -export interface RequestPasswordSaltParams { - userName: string; -} - -export interface ForgotPasswordParams { - userName: string; -} - -export interface ForgotPasswordChallengeParams extends ForgotPasswordParams { - email: string; -} - -export interface ForgotPasswordResetParams extends ForgotPasswordParams { - token: string; - newPassword: string; -} - -export interface AccountActivationParams extends ServerRegisterParams { - token: string; -} - -export interface ServerState { - initialized: boolean; - buddyList: User[]; - ignoreList: User[]; - info: ServerStateInfo; - status: ServerStateStatus; - logs: ServerStateLogs; - user: User; - users: User[]; - sortUsersBy: ServerStateSortUsersBy; - connectOptions: WebSocketConnectOptions; - messages: { - [userName: string]: UserMessageData[]; - } - userInfo: { - [userName: string]: User; - } - notifications: NotifyUserData[]; - serverShutdown: ServerShutdownData; - banUser: string; - banHistory: { - [userName: string]: BanHistoryItem[]; - }; - warnHistory: { - [userName: string]: WarnHistoryItem[]; - }; - warnListOptions: WarnListItem[]; - warnUser: string; - adminNotes: { [userName: string]: string }; - replays: ReplayMatch[]; - backendDecks: DeckList | null; -} - -export interface ServerStateStatus { - description: string; - state: number; -} - -export interface ServerStateInfo { - message: string; - name: string; - version: string; -} - -export interface ServerStateLogs { - room: LogItem[]; - game: LogItem[]; - chat: LogItem[]; -} - -export interface ServerStateSortUsersBy extends SortBy { - field: UserSortField -} diff --git a/webclient/src/store/server/server.reducer.ts b/webclient/src/store/server/server.reducer.ts deleted file mode 100644 index a63418eeb..000000000 --- a/webclient/src/store/server/server.reducer.ts +++ /dev/null @@ -1,481 +0,0 @@ -import { DeckStorageFolder, DeckStorageTreeItem, SortDirection, StatusEnum, UserLevelFlag, UserSortField } from 'types'; - -import { SortUtil } from '../common'; - -import { ServerState } from './server.interfaces' -import { Types } from './server.types'; - -function splitPath(path: string): string[] { - return path ? path.split('/') : []; -} - -function insertAtPath(folder: DeckStorageFolder, pathSegments: string[], item: DeckStorageTreeItem): DeckStorageFolder { - if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === '')) { - return { items: [...folder.items, item] }; - } - const [head, ...tail] = pathSegments; - const match = folder.items.find(child => child.name === head && child.folder); - if (match) { - return { - items: folder.items.map(child => - child === match - ? { ...child, folder: insertAtPath(child.folder!, tail, item) } - : child - ), - }; - } - const created: DeckStorageTreeItem = { id: 0, name: head, file: null, folder: insertAtPath({ items: [] }, tail, item) }; - return { items: [...folder.items, created] }; -} - -function removeById(folder: DeckStorageFolder, id: number): DeckStorageFolder { - return { - items: folder.items - .filter(item => item.id !== id) - .map(item => - item.folder ? { ...item, folder: removeById(item.folder, id) } : item - ), - }; -} - -function removeByPath(folder: DeckStorageFolder, pathSegments: string[]): DeckStorageFolder { - if (pathSegments.length === 0 || (pathSegments.length === 1 && pathSegments[0] === '')) { - return folder; - } - const [head, ...tail] = pathSegments; - if (tail.length === 0) { - return { items: folder.items.filter(item => !(item.name === head && item.folder !== null)) }; - } - return { - items: folder.items.map(item => - item.name === head && item.folder - ? { ...item, folder: removeByPath(item.folder, tail) } - : item - ), - }; -} - -const initialState: ServerState = { - initialized: false, - buddyList: [], - ignoreList: [], - - status: { - state: StatusEnum.DISCONNECTED, - description: null - }, - info: { - message: null, - name: null, - version: null - }, - logs: { - room: [], - game: [], - chat: [] - }, - user: null, - users: [], - sortUsersBy: { - field: UserSortField.NAME, - order: SortDirection.ASC - }, - connectOptions: {}, - messages: {}, - userInfo: {}, - notifications: [], - serverShutdown: null, - banUser: '', - banHistory: {}, - warnHistory: {}, - warnListOptions: [], - warnUser: '', - adminNotes: {}, - replays: [], - backendDecks: null, -}; - -export const serverReducer = (state = initialState, action: any) => { - switch (action.type) { - case Types.INITIALIZED: { - return { - ...initialState, - initialized: true - } - } - case Types.ACCOUNT_AWAITING_ACTIVATION: { - return { - ...state, - connectOptions: { - ...action.options - } - } - } - case Types.ACCOUNT_ACTIVATION_FAILED: - case Types.ACCOUNT_ACTIVATION_SUCCESS: { - return { - ...state, - connectOptions: {} - } - } - case Types.CLEAR_STORE: { - return { - ...initialState, - status: { - ...state.status - } - } - } - case Types.SERVER_MESSAGE: { - const { message } = action; - const { info } = state; - - return { - ...state, - info: { ...info, message } - } - } - case Types.UPDATE_BUDDY_LIST: { - const { buddyList } = action; - const { sortUsersBy } = state; - - SortUtil.sortUsersByField(buddyList, sortUsersBy); - - return { - ...state, - buddyList: [ - ...buddyList - ] - }; - } - case Types.ADD_TO_BUDDY_LIST: { - const { user } = action; - const { sortUsersBy } = state; - - const buddyList = [...state.buddyList]; - - buddyList.push(user); - SortUtil.sortUsersByField(buddyList, sortUsersBy); - - return { - ...state, - buddyList - }; - } - case Types.REMOVE_FROM_BUDDY_LIST: { - const { userName } = action; - const buddyList = state.buddyList.filter(user => user.name !== userName); - - return { - ...state, - buddyList - }; - } - case Types.UPDATE_IGNORE_LIST: { - const { ignoreList } = action; - const { sortUsersBy } = state; - - SortUtil.sortUsersByField(ignoreList, sortUsersBy); - - return { - ...state, - ignoreList: [ - ...ignoreList - ] - }; - } - case Types.ADD_TO_IGNORE_LIST: { - const { user } = action; - const { sortUsersBy } = state; - - const ignoreList = [...state.ignoreList]; - - ignoreList.push(user); - SortUtil.sortUsersByField(ignoreList, sortUsersBy); - - return { - ...state, - ignoreList - }; - } - case Types.REMOVE_FROM_IGNORE_LIST: { - const { userName } = action; - const ignoreList = state.ignoreList.filter(user => user.name !== userName); - - return { - ...state, - ignoreList - }; - } - case Types.UPDATE_INFO: { - const { name, version } = action.info; - const { info } = state; - - return { - ...state, - info: { ...info, name, version } - } - } - case Types.UPDATE_STATUS: { - const { status } = action; - - return { - ...state, - status: { ...status } - } - } - case Types.UPDATE_USER: - case Types.ACCOUNT_EDIT_CHANGED: - case Types.ACCOUNT_IMAGE_CHANGED: { - const { user } = action; - - return { - ...state, - user: { - ...state.user, - ...user - } - } - } - case Types.UPDATE_USERS: { - const users = [...action.users]; - const { sortUsersBy } = state; - - - SortUtil.sortUsersByField(users, sortUsersBy); - - return { - ...state, - users - }; - } - case Types.USER_JOINED: { - const { sortUsersBy } = state; - - const users = [ - ...state.users, - { ...action.user } - ]; - - SortUtil.sortUsersByField(users, sortUsersBy); - - return { - ...state, - users - }; - } - case Types.USER_LEFT: { - const { name } = action; - const users = state.users.filter(user => user.name !== name); - - return { - ...state, - users - }; - } - case Types.VIEW_LOGS: { - const { logs } = action; - - return { - ...state, - logs: { - ...logs - } - }; - } - case Types.CLEAR_LOGS: { - return { - ...state, - logs: { - ...initialState.logs - } - } - } - case Types.USER_MESSAGE: { - const { senderName, receiverName } = action.messageData; - const userName = state.user.name === senderName ? receiverName : senderName; - - return { - ...state, - messages: { - ...state.messages, - [userName]: [ - ...(state.messages[userName] ?? []), - action.messageData, - ], - } - }; - } - case Types.GET_USER_INFO: { - const { userInfo } = action; - - return { - ...state, - userInfo: { - ...state.userInfo, - [userInfo.name]: userInfo, - } - }; - } - case Types.NOTIFY_USER: { - const { notification } = action; - - return { - ...state, - notifications: [ - ...state.notifications, - notification - ] - }; - } - case Types.SERVER_SHUTDOWN: { - const { data } = action; - - return { - ...state, - serverShutdown: data, - }; - } - case Types.BAN_FROM_SERVER: { - const { userName } = action; - - return { - ...state, - banUser: userName, - }; - } - case Types.BAN_HISTORY: { - const { userName, banHistory } = action; - - return { - ...state, - banHistory: { - ...state.banHistory, - [userName]: banHistory, - } - }; - } - case Types.WARN_HISTORY: { - const { userName, warnHistory } = action; - - return { - ...state, - warnHistory: { - ...state.warnHistory, - [userName]: warnHistory, - } - }; - } - case Types.WARN_LIST_OPTIONS: { - const { warnList } = action; - - return { - ...state, - warnListOptions: warnList, - }; - } - case Types.WARN_USER: { - const { userName } = action; - return { - ...state, - warnUser: userName, - }; - } - case Types.GET_ADMIN_NOTES: - case Types.UPDATE_ADMIN_NOTES: { - const { userName, notes } = action; - return { - ...state, - adminNotes: { - ...state.adminNotes, - [userName]: notes, - } - }; - } - case Types.ADJUST_MOD: { - const { userName, shouldBeMod, shouldBeJudge } = action; - - return { - ...state, - users: state.users.map((user) => { - if (user.name !== userName) { - return user; - } - const judgeFlag = shouldBeJudge ? UserLevelFlag.IsJudge : UserLevelFlag.IsNothing; - const modFlag = shouldBeMod ? UserLevelFlag.IsModerator : UserLevelFlag.IsNothing; - return { - ...user, - userLevel: user.userLevel & (judgeFlag | modFlag) - } - }) - }; - } - case Types.REPLAY_LIST: { - return { ...state, replays: [...action.matchList] }; - } - case Types.REPLAY_ADDED: { - return { ...state, replays: [...state.replays, action.matchInfo] }; - } - case Types.REPLAY_MODIFY_MATCH: { - return { - ...state, - replays: state.replays.map(r => - r.gameId === action.gameId ? { ...r, doNotHide: action.doNotHide } : r - ), - }; - } - case Types.REPLAY_DELETE_MATCH: { - return { ...state, replays: state.replays.filter(r => r.gameId !== action.gameId) }; - } - case Types.BACKEND_DECKS: { - return { ...state, backendDecks: action.deckList }; - } - case Types.DECK_UPLOAD: { - if (!state.backendDecks) { - return state; - } - return { - ...state, - backendDecks: { - root: insertAtPath(state.backendDecks.root, splitPath(action.path), action.treeItem), - }, - }; - } - case Types.DECK_DELETE: { - if (!state.backendDecks) { - return state; - } - return { - ...state, - backendDecks: { - root: removeById(state.backendDecks.root, action.deckId), - }, - }; - } - case Types.DECK_NEW_DIR: { - if (!state.backendDecks) { - return state; - } - const newFolder: DeckStorageTreeItem = { id: 0, name: action.dirName, file: null, folder: { items: [] } }; - return { - ...state, - backendDecks: { - root: insertAtPath(state.backendDecks.root, splitPath(action.path), newFolder), - }, - }; - } - case Types.DECK_DEL_DIR: { - if (!state.backendDecks) { - return state; - } - return { - ...state, - backendDecks: { - root: removeByPath(state.backendDecks.root, splitPath(action.path)), - }, - }; - } - default: - return state; - } -} diff --git a/webclient/src/store/server/server.selectors.ts b/webclient/src/store/server/server.selectors.ts deleted file mode 100644 index fa9f82297..000000000 --- a/webclient/src/store/server/server.selectors.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ServerState } from './server.interfaces'; - -interface State { - server: ServerState -} - -export const Selectors = { - getInitialized: ({ server }: State) => server.initialized, - getConnectOptions: ({ server }: State) => server.connectOptions, - getMessage: ({ server }: State) => server.info.message, - getName: ({ server }: State) => server.info.name, - getVersion: ({ server }: State) => server.info.version, - getDescription: ({ server }: State) => server.status.description, - getState: ({ server }: State) => server.status.state, - getUser: ({ server }: State) => server.user, - getUsers: ({ server }: State) => server.users, - getLogs: ({ server }: State) => server.logs, - getBuddyList: ({ server }: State) => server.buddyList, - getIgnoreList: ({ server }: State) => server.ignoreList, - getReplays: ({ server }: State) => server.replays, - getBackendDecks: ({ server }: State) => server.backendDecks, -} diff --git a/webclient/src/store/server/server.types.ts b/webclient/src/store/server/server.types.ts deleted file mode 100644 index fb4249011..000000000 --- a/webclient/src/store/server/server.types.ts +++ /dev/null @@ -1,72 +0,0 @@ -export const Types = { - INITIALIZED: '[Server] Initialized', - CLEAR_STORE: '[Server] Clear Store', - LOGIN_SUCCESSFUL: '[Server] Login Successful', - LOGIN_FAILED: '[Server] Login Failed', - CONNECTION_CLOSED: '[Server] Connection Closed', - CONNECTION_FAILED: '[Server] Connection Failed', - TEST_CONNECTION_SUCCESSFUL: '[Server] Test Connection Successful', - TEST_CONNECTION_FAILED: '[Server] Test Connection Failed', - SERVER_MESSAGE: '[Server] Server Message', - UPDATE_BUDDY_LIST: '[Server] Update Buddy List', - ADD_TO_BUDDY_LIST: '[Server] Add to Buddy List', - REMOVE_FROM_BUDDY_LIST: '[Server] Remove from Buddy List', - UPDATE_IGNORE_LIST: '[Server] Update Ignore List', - ADD_TO_IGNORE_LIST: '[Server] Add to Ignore List', - REMOVE_FROM_IGNORE_LIST: '[Server] Remove from Ignore List', - UPDATE_INFO: '[Server] Update Info', - UPDATE_STATUS: '[Server] Update Status', - UPDATE_USER: '[Server] Update User', - UPDATE_USERS: '[Server] Update Users', - USER_JOINED: '[Server] User Joined', - USER_LEFT: '[Server] User Left', - VIEW_LOGS: '[Server] View Logs', - CLEAR_LOGS: '[Server] Clear Logs', - REGISTRATION_REQUIRES_EMAIL: '[Server] Registration Requires Email', - REGISTRATION_SUCCES: '[Server] Registration Success', - REGISTRATION_FAILED: '[Server] Registration Failed', - REGISTRATION_EMAIL_ERROR: '[Server] Registration Email Error', - REGISTRATION_PASSWORD_ERROR: '[Server] Registration Password Error', - REGISTRATION_USERNAME_ERROR: '[Server] Registration Username Error', - ACCOUNT_AWAITING_ACTIVATION: '[Server] Account Awaiting Activation', - ACCOUNT_ACTIVATION_SUCCESS: '[Server] Account Activation Success', - ACCOUNT_ACTIVATION_FAILED: '[Server] Account Activation Failed', - RESET_PASSWORD_REQUESTED: '[Server] Reset Password Requested', - RESET_PASSWORD_FAILED: '[Server] Reset Password Failed', - RESET_PASSWORD_CHALLENGE: '[Server] Reset Password Challenge', - RESET_PASSWORD_SUCCESS: '[Server] Reset Password Success', - ADJUST_MOD: '[Server] Adjust Mod', - RELOAD_CONFIG: '[Server] Reload Config', - SHUTDOWN_SERVER: '[Server] Shutdown Server', - UPDATE_SERVER_MESSAGE: '[Server] Update Server Message', - ACCOUNT_PASSWORD_CHANGE: '[Server] Account Password Change', - ACCOUNT_EDIT_CHANGED: '[Server] Account Edit Changed', - ACCOUNT_IMAGE_CHANGED: '[Server] Account Image Changed', - DIRECT_MESSAGE_SENT: '[Server] Direct Message Sent', - GET_USER_INFO: '[Server] Get User Info', - NOTIFY_USER: '[Server] Notify User', - SERVER_SHUTDOWN: '[Server] Server Shutdown', - USER_MESSAGE: '[Server] User Message', - ADD_TO_LIST: '[Server] Add To List', - REMOVE_FROM_LIST: '[Server] Remove From List', - BAN_FROM_SERVER: '[Server] Ban From Server', - BAN_HISTORY: '[Server] Ban History', - WARN_HISTORY: '[Server] Warn History', - WARN_LIST_OPTIONS: '[Server] Warn List Options', - WARN_USER: '[Server] Warn User', - GRANT_REPLAY_ACCESS: '[Server] Grant Replay Access', - FORCE_ACTIVATE_USER: '[Server] Force Activate User', - GET_ADMIN_NOTES: '[Server] Get Admin Notes', - UPDATE_ADMIN_NOTES: '[Server] Update Admin Notes', - // Replay - REPLAY_LIST: '[Server] Replay List', - REPLAY_ADDED: '[Server] Replay Added', - REPLAY_MODIFY_MATCH: '[Server] Replay Modify Match', - REPLAY_DELETE_MATCH: '[Server] Replay Delete Match', - // Deck Storage - BACKEND_DECKS: '[Server] Backend Decks', - DECK_NEW_DIR: '[Server] Deck New Dir', - DECK_DEL_DIR: '[Server] Deck Del Dir', - DECK_UPLOAD: '[Server] Deck Upload', - DECK_DELETE: '[Server] Deck Delete', -}; diff --git a/webclient/src/store/store.ts b/webclient/src/store/store.ts deleted file mode 100644 index e6ef5df3a..000000000 --- a/webclient/src/store/store.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createStore, applyMiddleware } from 'redux'; -import thunk from 'redux-thunk'; -import rootReducer from './rootReducer'; - -const initialState = {}; - -const middleware: any = [thunk]; - -export const store = createStore(rootReducer, initialState, applyMiddleware(...middleware)); diff --git a/webclient/src/types/cards.ts b/webclient/src/types/cards.ts deleted file mode 100644 index b28bae203..000000000 --- a/webclient/src/types/cards.ts +++ /dev/null @@ -1,93 +0,0 @@ -export class Card { - artist: string; - availability: string[]; - borderColor: string; - colorIdentity: string[]; - colors: string[]; - convertedManaCost: number; - edhrecRank: number; - flavorText: string; - identifiers: { - cardKingdomId: string; - mcmId: string; - mcmMetaId: string; - mtgjsonV4Id: string; - multiverseId: string; - scryfallId: string; - scryfallIllustrationId: string; - scryfallOracleId: string; - tcgplayerProductId: string; - }; - isOnlineOnly: boolean; - layout: string; - legalities: { - brawl: string; - commander: string; - duel: string; - future: string; - gladiator: string; - historic: string; - legacy: string; - modern: string; - pauper: string; - penny: string; - pioneer: string; - premodern: string; - standard: string; - vintage: string; - }; - manaCost: string; - name: string; - originalText: string; - originalType: string; - power: string; - printings: string[]; - rarity: string; - rulings: { - date: string; - text: string; - }[]; - side: string; - setCode: string; - subtypes: string[]; - supertypes: string[]; - text: string; - toughness: string; - type: string; - types: string[]; - uuid: string; - variations: string[]; -} - -export class Set { - baseSetSize: number; - block: string; - cards: string[]; - code: string; - isOnlineOnly: boolean; - name: string; - releaseDate: string; - totalSetSize: number; - type: string; -} - -export class Token { - name: { value: string }; - prop: { - value: { - cmc: { value: string; }; - colors: { value: string; }; - maintype: { value: string; }; - pt: { value: string; }; - type: { value: string; }; - }; - }; - related: { value: string; }[]; - 'reverse-related': { value: string; }[]; - set: { - value: string; - picURL: string; - }[]; - tablerow: { value: string; }; - text: { value: string; }; -} diff --git a/webclient/src/types/constants.spec.ts b/webclient/src/types/constants.spec.ts deleted file mode 100644 index f95a16eaa..000000000 --- a/webclient/src/types/constants.spec.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - URL_REGEX, - MESSAGE_SENDER_REGEX, - MENTION_REGEX, - CARD_CALLOUT_REGEX, - CALLOUT_BOUNDARY_REGEX, -} from './constants'; - -describe('RegEx', () => { - describe('URL_REGEX', () => { - it('should match and capture whole url in main capture group', () => { - const test = [ - 'http://example.com', - 'https://example.com', - 'https://www.example.com', - ]; - - test.forEach(str => { - const match = str.match(URL_REGEX); - - expect(match).toBeDefined(); - expect(match[0]).toBe(str); - }); - }); - - it('should not match bad urls', () => { - const test = [ - 'htt://example.com', - 'https:/example.com', - 'https//www.example.com', - 'www.example.com', - 'example.com', - ]; - - test.forEach(str => - expect(str.match(URL_REGEX)).toBe(null) - ); - }); - }); - - describe('MESSAGE_SENDER_REGEX', () => { - it('should match and capture sender name in second capture group', () => { - const sender = 'sender'; - const match = `${sender}: message`.match(MESSAGE_SENDER_REGEX); - - expect(match).toBeDefined(); - expect(match[1]).toBe(sender); - }); - - it('should not match if spaces before :', () => { - const test = [ - ' sender: message', - 'sender : message', - ' sender : message', - ]; - - test.forEach(str => - expect(str.match(URL_REGEX)).toBe(null) - ); - }); - }); - - describe('MENTION_REGEX', () => { - it('should match and capture user mentions in second capture group', () => { - expect('@mention'.match(MENTION_REGEX)[0]).toBe('@mention'); - expect('@mention '.match(MENTION_REGEX)[0]).toBe('@mention'); - expect(' @mention'.match(MENTION_REGEX)[0]).toBe(' @mention'); - expect(' @mention '.match(MENTION_REGEX)[0]).toBe(' @mention'); - expect('leading @mention'.match(MENTION_REGEX)[0]).toBe(' @mention'); - expect('leading @mention trailing'.match(MENTION_REGEX)[0]).toBe(' @mention'); - expect('@mention trailing'.match(MENTION_REGEX)[0]).toBe('@mention'); - }); - - it('should not match preceded by character', () => { - const test = [ - 'leading@mention', - ]; - - test.forEach(str => - expect(str.match(MENTION_REGEX)).toBe(null) - ); - }); - }); -}); diff --git a/webclient/src/types/constants.ts b/webclient/src/types/constants.ts deleted file mode 100644 index 7341a22b1..000000000 --- a/webclient/src/types/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -// eslint-disable-next-line -export const URL_REGEX = /(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b(?:[-a-zA-Z0-9@:%_\+.~#?&//=]*))/g; -export const MESSAGE_SENDER_REGEX = /(^[^:\s]+):/; -export const MENTION_REGEX = /(^|\s)(@\w+)/g; -export const CARD_CALLOUT_REGEX = /(\[\[[^\]]+\]\])/g; -export const CALLOUT_BOUNDARY_REGEX = /(\[\[|\]\])/g; diff --git a/webclient/src/types/countries.ts b/webclient/src/types/countries.ts deleted file mode 100644 index 0289b63ad..000000000 --- a/webclient/src/types/countries.ts +++ /dev/null @@ -1,253 +0,0 @@ -export const countryCodes = [ - 'AF', - 'AX', - 'AL', - 'DZ', - 'AS', - 'AD', - 'AO', - 'AI', - 'AQ', - 'AG', - 'AR', - 'AM', - 'AW', - 'AU', - 'AT', - 'AZ', - 'BH', - 'BS', - 'BD', - 'BB', - 'BY', - 'BE', - 'BZ', - 'BJ', - 'BM', - 'BT', - 'BO', - 'BQ', - 'BA', - 'BW', - 'BV', - 'BR', - 'IO', - 'BN', - 'BG', - 'BF', - 'BI', - 'KH', - 'CM', - 'CA', - 'CV', - 'KY', - 'CF', - 'TD', - 'CL', - 'CN', - 'CX', - 'CC', - 'CO', - 'KM', - 'CG', - 'CD', - 'CK', - 'CR', - 'CI', - 'HR', - 'CU', - 'CW', - 'CY', - 'CZ', - 'DK', - 'DJ', - 'DM', - 'DO', - 'EC', - 'EG', - 'SV', - 'GQ', - 'ER', - 'EE', - 'ET', - 'EU', - 'FK', - 'FO', - 'FJ', - 'FI', - 'FR', - 'GF', - 'PF', - 'TF', - 'GA', - 'GM', - 'GE', - 'DE', - 'GH', - 'GI', - 'GR', - 'GL', - 'GD', - 'GP', - 'GU', - 'GT', - 'GG', - 'GN', - 'GW', - 'GY', - 'HT', - 'HM', - 'VA', - 'HN', - 'HK', - 'HU', - 'IS', - 'IN', - 'ID', - 'IR', - 'IQ', - 'IE', - 'IM', - 'IL', - 'IT', - 'JM', - 'JP', - 'JE', - 'JO', - 'KZ', - 'KE', - 'KI', - 'KP', - 'KR', - 'KW', - 'KG', - 'LA', - 'LV', - 'LB', - 'LS', - 'LR', - 'LY', - 'LI', - 'LT', - 'LU', - 'MO', - 'MK', - 'MG', - 'MW', - 'MY', - 'MV', - 'ML', - 'MT', - 'MH', - 'MQ', - 'MR', - 'MU', - 'YT', - 'MX', - 'FM', - 'MD', - 'MC', - 'MN', - 'ME', - 'MS', - 'MA', - 'MZ', - 'MM', - 'NA', - 'NR', - 'NP', - 'NL', - 'NC', - 'NZ', - 'NI', - 'NE', - 'NG', - 'NU', - 'NF', - 'MP', - 'NO', - 'OM', - 'PK', - 'PW', - 'PS', - 'PA', - 'PG', - 'PY', - 'PE', - 'PH', - 'PN', - 'PL', - 'PT', - 'PR', - 'QA', - 'RE', - 'RO', - 'RU', - 'RW', - 'BL', - 'SH', - 'KN', - 'LC', - 'MF', - 'PM', - 'VC', - 'WS', - 'SM', - 'ST', - 'SA', - 'SN', - 'RS', - 'SC', - 'SL', - 'SG', - 'SX', - 'SK', - 'SI', - 'SB', - 'SO', - 'ZA', - 'GS', - 'SS', - 'ES', - 'LK', - 'SD', - 'SR', - 'SJ', - 'SZ', - 'SE', - 'CH', - 'SY', - 'TW', - 'TJ', - 'TZ', - 'TH', - 'TL', - 'TG', - 'TK', - 'TO', - 'TT', - 'TN', - 'TR', - 'TM', - 'TC', - 'TV', - 'UG', - 'UA', - 'AE', - 'GB', - 'US', - 'UM', - 'UY', - 'UZ', - 'VU', - 'VE', - 'VN', - 'VG', - 'VI', - 'WF', - 'EH', - 'YE', - 'XK', - 'ZM', - 'ZW', -]; diff --git a/webclient/src/types/deckList.ts b/webclient/src/types/deckList.ts deleted file mode 100644 index 9d7d792a6..000000000 --- a/webclient/src/types/deckList.ts +++ /dev/null @@ -1,18 +0,0 @@ -export interface DeckList { - root: DeckStorageFolder; -} - -export interface DeckStorageFolder { - items: DeckStorageTreeItem[]; -} - -export interface DeckStorageFile { - creationTime: number; -} - -export interface DeckStorageTreeItem { - id: number; - name: string; - file: DeckStorageFile | null; - folder: DeckStorageFolder | null; -} diff --git a/webclient/src/types/forms.ts b/webclient/src/types/forms.ts deleted file mode 100644 index 421bc2615..000000000 --- a/webclient/src/types/forms.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum FormKey { - ADD_TO_BUDDIES = 'ADD_TO_BUDDIES', - ADD_TO_IGNORE = 'ADD_TO_IGNORE', - CARD_IMPORT = 'CARD_IMPORT', - CONNECT = 'CONNECT', - LOGIN = 'LOGIN', - RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST', - RESET_PASSWORD = 'RESET_PASSWORD', - REGISTER = 'REGISTER', - SEARCH_LOGS = 'SEARCH_LOGS', -} diff --git a/webclient/src/types/game.ts b/webclient/src/types/game.ts deleted file mode 100644 index b9fcc1dc2..000000000 --- a/webclient/src/types/game.ts +++ /dev/null @@ -1,42 +0,0 @@ -export interface Game { - description: string; - gameId: number; - gameType: string; - gameTypes: string[]; - roomId: number; - started: boolean; -} - -export enum GameSortField { - START_TIME = 'startTime' -} - -export interface GameConfig { - description: string; - password: string; - maxPlayer: number; - onlyBuddies: boolean; - onlyRegistered: boolean; - spectatorsAllowed: boolean; - spectatorsNeedPassword: boolean; - spectatorsCanTalk: boolean; - spectatorsSeeEverything: boolean; - gameTypeIds: number[]; - joinAsJudge: boolean; - joinAsSpectator: boolean; -} - -export interface JoinGameParams { - gameId: number; - password: string; - spectator: boolean; - overrideRestrictions: boolean; - joinAsJudge: boolean; -} - -export enum LeaveGameReason { - OTHER = 1, - USER_KICKED = 2, - USER_LEFT = 3, - USER_DISCONNECTED = 4 -} diff --git a/webclient/src/types/index.ts b/webclient/src/types/index.ts deleted file mode 100644 index ded9962e5..000000000 --- a/webclient/src/types/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -export * from './cards'; -export * from './constants'; -export * from './countries'; -export * from './game'; -export * from './room'; -export * from './server'; -export * from './sort'; -export * from './user'; -export * from './routes'; -export * from './sort'; -export * from './forms'; -export * from './message'; -export * from './settings'; -export * from './languages'; -export * from './logs'; -export * from './session'; -export * from './deckList'; -export * from './moderator'; -export * from './replay'; diff --git a/webclient/src/types/languages.ts b/webclient/src/types/languages.ts deleted file mode 100644 index 17699fd0e..000000000 --- a/webclient/src/types/languages.ts +++ /dev/null @@ -1,20 +0,0 @@ -export enum Language { - 'en-US' = 'en-US', - 'fr' = 'fr', - 'nl' = 'nl', - 'pt_BR' = 'pt_BR', -} - -export enum LanguageCountry { - 'en-US' = 'us', - 'fr' = 'fr', - 'nl' = 'nl', - 'pt_BR' = 'br' -} - -export enum LanguageNative { - 'en-US' = 'English - US', - 'fr' = 'Français', - 'nl' = 'Nederlands', - 'pt_BR' = 'Portugues do Brasil', -} diff --git a/webclient/src/types/logs.ts b/webclient/src/types/logs.ts deleted file mode 100644 index 3cf34b486..000000000 --- a/webclient/src/types/logs.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface LogFilters { - userName?: string; - ipAddress?: string; - gameName?: string; - gameId?: string; - message?: string; - logLocation?: string[]; - dateRange: number; - maximumResults?: number; -} diff --git a/webclient/src/types/message.ts b/webclient/src/types/message.ts deleted file mode 100644 index d7f256011..000000000 --- a/webclient/src/types/message.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface Message { - name: string; - message: string; - messageType: number; - timeOf: number; - timeReceived: number; -} diff --git a/webclient/src/types/moderator.ts b/webclient/src/types/moderator.ts deleted file mode 100644 index 5991ec6c1..000000000 --- a/webclient/src/types/moderator.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface BanHistoryItem { - adminId: string; - adminName: string; - banTime: string; - banLength: string; - banReason: string; - visibleReason: string; -} - -export interface WarnHistoryItem { - userName: string; - adminName: string; - reason: string; - timeOf: string; -} - -export interface WarnListItem { - warning: string; - userName: string; - userClientid: string; -} diff --git a/webclient/src/types/replay.ts b/webclient/src/types/replay.ts deleted file mode 100644 index dfa78538a..000000000 --- a/webclient/src/types/replay.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface Replay { - replayId: number; - replayName: string; - duration: number; -} - -export interface ReplayMatch { - replayList: Replay[]; - gameId: number; - roomName: string; - timeStarted: number; - length: number; - gameName: string; - playerNames: string[]; - doNotHide: boolean; -} diff --git a/webclient/src/types/room.ts b/webclient/src/types/room.ts deleted file mode 100644 index e69b1a499..000000000 --- a/webclient/src/types/room.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { User } from './user'; - -export interface Room { - autoJoin: boolean - description: string; - gameCount: number; - gameList: any[]; - gametypeList: any[]; - gametypeMap: GametypeMap; - name: string; - permissionlevel: RoomAccessLevel; - playerCount: number; - privilegelevel: RoomAccessLevel; - roomId: number; - userList: User[]; - order: number; -} - -export interface GametypeMap { [index: number]: string } - -export enum RoomAccessLevel { - 'none' -} diff --git a/webclient/src/types/routes.ts b/webclient/src/types/routes.ts deleted file mode 100644 index d91e8a7cb..000000000 --- a/webclient/src/types/routes.ts +++ /dev/null @@ -1,15 +0,0 @@ -export enum RouteEnum { - PLAYER = '/player/:name', - SERVER = '/server', - ROOM = '/room/:roomId', - LOGIN = '/login', - LOGS = '/logs', - GAME = '/game', - DECKS = '/decks', - DECK = '/deck', - ACCOUNT = '/account', - ADMINISTRATION = '/administration', - REPLAYS = '/replays', - INITIALIZE = '/initialize', - UNSUPPORTED = '/unsupported', -} diff --git a/webclient/src/types/server.ts b/webclient/src/types/server.ts deleted file mode 100644 index 305a5a810..000000000 --- a/webclient/src/types/server.ts +++ /dev/null @@ -1,127 +0,0 @@ -export interface ServerStatus { - status: StatusEnum; - description: string; -} - -export enum StatusEnum { - DISCONNECTED, - CONNECTING, - CONNECTED, - LOGGING_IN, - LOGGED_IN, - DISCONNECTING = 99 -} - -export interface WebSocketConnectOptions { - host?: string; - port?: string; - userName?: string; - password?: string; - hashedPassword?: string; - newPassword?: string; - token?: string; - email?: string; - realName?: string; - country?: string; - autojoinrooms?: boolean; - keepalive?: number; - clientid?: string; - reason?: WebSocketConnectReason; -} - -export enum WebSocketConnectReason { - LOGIN, - REGISTER, - ACTIVATE_ACCOUNT, - PASSWORD_RESET_REQUEST, - PASSWORD_RESET_CHALLENGE, - PASSWORD_RESET, - TEST_CONNECTION, -} - -export class Host { - id?: number; - name: string; - host: string; - port: string; - localHost?: string; - localPort?: string; - editable: boolean; - lastSelected?: boolean; - userName?: string; - hashedPassword?: string; - remember?: boolean; -} - -export const DefaultHosts: Host[] = [ - { - name: 'Chickatrice', - host: 'mtg.chickatrice.net', - port: '443', - localPort: '4748', - editable: false, - }, - { - name: 'Rooster', - host: 'server.cockatrice.us/servatrice', - port: '4748', - localHost: 'server.cockatrice.us', - editable: false, - }, - { - name: 'Rooster Beta', - host: 'beta.cockatrice.us/servatrice', - port: '4748', - localHost: 'beta.cockatrice.us', - editable: false, - }, - { - name: 'Tetrarch', - host: 'mtg.tetrarch.co/servatrice', - port: '443', - editable: false, - }, -]; - -export const getHostPort = (host: Host): { host: string, port: string } => { - const isLocal = window.location.hostname === 'localhost'; - - if (!host) { - return { - host: '', - port: '' - }; - } - - return { - host: !isLocal ? host.host : host.localHost || host.host, - port: !isLocal ? host.port : host.localPort || host.port, - } -}; - -export enum KnownHost { - ROOSTER = 'Rooster', - TETRARCH = 'Tetrarch', -} - -export const KnownHosts = { - [KnownHost.ROOSTER]: { port: 4748, host: 'server.cockatrice.us', }, - [KnownHost.TETRARCH]: { port: 443, host: 'mtg.tetrarch.co/servatrice' }, -} - -export interface LogItem { - message: string; - senderId: string; - senderIp: string; - senderName: string; - targetId: string; - targetName: string; - targetType: string; - time: string; -} - -export interface LogGroups { - room: LogItem[]; - game: LogItem[]; - chat: LogItem[]; -} diff --git a/webclient/src/types/session.ts b/webclient/src/types/session.ts deleted file mode 100644 index 1b49616c5..000000000 --- a/webclient/src/types/session.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum NotificationType { - UNKNOWN = 0, - PROMOTED = 1, - WARNING = 2, - IDLEWARNING = 3, - CUSTOM = 4, -}; diff --git a/webclient/src/types/settings.ts b/webclient/src/types/settings.ts deleted file mode 100644 index c91394331..000000000 --- a/webclient/src/types/settings.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class Setting { - user: string; - autoConnect?: boolean; -} - -export const APP_USER = '*app'; diff --git a/webclient/src/types/sort.ts b/webclient/src/types/sort.ts deleted file mode 100644 index c3312732c..000000000 --- a/webclient/src/types/sort.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum SortDirection { - ASC = 'ASC', - DESC = 'DESC' -} - -export interface SortBy { - field: string; - order: SortDirection; -} diff --git a/webclient/src/types/user.ts b/webclient/src/types/user.ts deleted file mode 100644 index 2d0383eb7..000000000 --- a/webclient/src/types/user.ts +++ /dev/null @@ -1,28 +0,0 @@ -export interface User { - accountageSecs: number; - name: string; - privlevel: UserPrivLevel; - userLevel: number; - realName?: string; - country?: string; - avatarBmp?: Uint8Array; -} - -export enum UserLevelFlag { - IsNothing = 0, - IsUser = 1, - IsRegistered = 2, - IsModerator = 4, - IsAdmin = 8, - IsJudge = 16, -} - -export enum UserPrivLevel { - NONE = 0, - VIP = 1, - DONOR = 2 -} - -export enum UserSortField { - NAME = 'name' -} diff --git a/webclient/src/websocket/WebClient.ts b/webclient/src/websocket/WebClient.ts deleted file mode 100644 index 1dae24d0c..000000000 --- a/webclient/src/websocket/WebClient.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import { ProtobufService } from './services/ProtobufService'; -import { WebSocketService } from './services/WebSocketService'; - -import { RoomPersistence, SessionPersistence } from './persistence'; - -export class WebClient { - public socket = new WebSocketService(this); - public protobuf = new ProtobufService(this); - - public protocolVersion = 14; - public clientConfig = { - clientid: 'webatrice', - clientver: 'webclient-1.0 (2019-10-31)', - clientfeatures: [ - 'client_id', - 'client_ver', - 'feature_set', - 'room_chat_history', - 'client_warnings', - /* unimplemented features */ - 'forgot_password', - 'idle_client', - 'mod_log_lookup', - 'user_ban_history', - // satisfy server reqs for POC - 'websocket', - '2.7.0_min_version', - '2.8.0_min_version' - ] - }; - - public clientOptions = { - autojoinrooms: true, - keepalive: 5000 - }; - - public options: WebSocketConnectOptions; - public status: StatusEnum; - - public connectionAttemptMade = false; - - constructor() { - this.socket.message$.subscribe((message: MessageEvent) => { - this.protobuf.handleMessageEvent(message); - }); - - if (process.env.NODE_ENV !== 'test') { - console.log(this); - } - } - - public connect(options: WebSocketConnectOptions) { - this.connectionAttemptMade = true; - this.options = options; - this.socket.connect(options); - } - - public testConnect(options: WebSocketConnectOptions) { - this.socket.testConnect(options); - } - - public disconnect() { - this.socket.disconnect(); - } - - public updateStatus(status: StatusEnum) { - this.status = status; - - if (status === StatusEnum.DISCONNECTED) { - this.protobuf.resetCommands(); - this.clearStores(); - } - } - - public keepAlive(pingReceived: Function) { - this.protobuf.sendKeepAliveCommand(pingReceived); - } - - private clearStores() { - RoomPersistence.clearStore(); - SessionPersistence.clearStore(); - } -} - -const webClient = new WebClient(); - -export default webClient; diff --git a/webclient/src/websocket/commands/admin/adjustMod.ts b/webclient/src/websocket/commands/admin/adjustMod.ts deleted file mode 100644 index fd71fece1..000000000 --- a/webclient/src/websocket/commands/admin/adjustMod.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { AdminPersistence } from '../../persistence'; - -export function adjustMod(userName: string, shouldBeMod?: boolean, shouldBeJudge?: boolean): void { - BackendService.sendAdminCommand('Command_AdjustMod', { userName, shouldBeMod, shouldBeJudge }, { - onSuccess: () => { - AdminPersistence.adjustMod(userName, shouldBeMod, shouldBeJudge); - }, - }); -} diff --git a/webclient/src/websocket/commands/admin/index.ts b/webclient/src/websocket/commands/admin/index.ts deleted file mode 100644 index 5d4ae21bf..000000000 --- a/webclient/src/websocket/commands/admin/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './adjustMod'; -export * from './reloadConfig'; -export * from './shutdownServer'; -export * from './updateServerMessage'; diff --git a/webclient/src/websocket/commands/admin/reloadConfig.ts b/webclient/src/websocket/commands/admin/reloadConfig.ts deleted file mode 100644 index 979f3ec73..000000000 --- a/webclient/src/websocket/commands/admin/reloadConfig.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { AdminPersistence } from '../../persistence'; - -export function reloadConfig(): void { - BackendService.sendAdminCommand('Command_ReloadConfig', {}, { - onSuccess: () => { - AdminPersistence.reloadConfig(); - }, - }); -} diff --git a/webclient/src/websocket/commands/admin/shutdownServer.ts b/webclient/src/websocket/commands/admin/shutdownServer.ts deleted file mode 100644 index e65c900db..000000000 --- a/webclient/src/websocket/commands/admin/shutdownServer.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { AdminPersistence } from '../../persistence'; - -export function shutdownServer(reason: string, minutes: number): void { - BackendService.sendAdminCommand('Command_ShutdownServer', { reason, minutes }, { - onSuccess: () => { - AdminPersistence.shutdownServer(); - }, - }); -} diff --git a/webclient/src/websocket/commands/admin/updateServerMessage.ts b/webclient/src/websocket/commands/admin/updateServerMessage.ts deleted file mode 100644 index e2b194514..000000000 --- a/webclient/src/websocket/commands/admin/updateServerMessage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { AdminPersistence } from '../../persistence'; - -export function updateServerMessage(): void { - BackendService.sendAdminCommand('Command_UpdateServerMessage', {}, { - onSuccess: () => { - AdminPersistence.updateServerMessage(); - }, - }); -} diff --git a/webclient/src/websocket/commands/index.ts b/webclient/src/websocket/commands/index.ts deleted file mode 100644 index 2c68fcfb9..000000000 --- a/webclient/src/websocket/commands/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * as AdminCommands from './admin'; -export * as ModeratorCommands from './moderator'; -export * as RoomCommands from './room'; -export * as SessionCommands from './session'; diff --git a/webclient/src/websocket/commands/moderator/banFromServer.ts b/webclient/src/websocket/commands/moderator/banFromServer.ts deleted file mode 100644 index e45e34504..000000000 --- a/webclient/src/websocket/commands/moderator/banFromServer.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function banFromServer(minutes: number, userName?: string, address?: string, reason?: string, - visibleReason?: string, clientid?: string, removeMessages?: number): void { - BackendService.sendModeratorCommand('Command_BanFromServer', { - minutes, userName, address, reason, visibleReason, clientid, removeMessages - }, { - onSuccess: () => { - ModeratorPersistence.banFromServer(userName); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/forceActivateUser.ts b/webclient/src/websocket/commands/moderator/forceActivateUser.ts deleted file mode 100644 index d4138a015..000000000 --- a/webclient/src/websocket/commands/moderator/forceActivateUser.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function forceActivateUser(usernameToActivate: string, moderatorName: string): void { - BackendService.sendModeratorCommand('Command_ForceActivateUser', { usernameToActivate, moderatorName }, { - onSuccess: () => { - ModeratorPersistence.forceActivateUser(usernameToActivate, moderatorName); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/getAdminNotes.ts b/webclient/src/websocket/commands/moderator/getAdminNotes.ts deleted file mode 100644 index d4f626aa2..000000000 --- a/webclient/src/websocket/commands/moderator/getAdminNotes.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function getAdminNotes(userName: string): void { - BackendService.sendModeratorCommand('Command_GetAdminNotes', { userName }, { - responseName: 'Response_GetAdminNotes', - onSuccess: (response) => { - ModeratorPersistence.getAdminNotes(userName, response.notes); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/getBanHistory.ts b/webclient/src/websocket/commands/moderator/getBanHistory.ts deleted file mode 100644 index dd4e90eda..000000000 --- a/webclient/src/websocket/commands/moderator/getBanHistory.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function getBanHistory(userName: string): void { - BackendService.sendModeratorCommand('Command_GetBanHistory', { userName }, { - responseName: 'Response_BanHistory', - onSuccess: (response) => { - ModeratorPersistence.banHistory(userName, response.banList); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/getWarnHistory.ts b/webclient/src/websocket/commands/moderator/getWarnHistory.ts deleted file mode 100644 index c47e2c6e4..000000000 --- a/webclient/src/websocket/commands/moderator/getWarnHistory.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function getWarnHistory(userName: string): void { - BackendService.sendModeratorCommand('Command_GetWarnHistory', { userName }, { - responseName: 'Response_WarnHistory', - onSuccess: (response) => { - ModeratorPersistence.warnHistory(userName, response.warnList); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/getWarnList.ts b/webclient/src/websocket/commands/moderator/getWarnList.ts deleted file mode 100644 index 412aee09e..000000000 --- a/webclient/src/websocket/commands/moderator/getWarnList.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function getWarnList(modName: string, userName: string, userClientid: string): void { - BackendService.sendModeratorCommand('Command_GetWarnList', { modName, userName, userClientid }, { - responseName: 'Response_WarnList', - onSuccess: (response) => { - ModeratorPersistence.warnListOptions(response.warning); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/grantReplayAccess.ts b/webclient/src/websocket/commands/moderator/grantReplayAccess.ts deleted file mode 100644 index 74d64d17a..000000000 --- a/webclient/src/websocket/commands/moderator/grantReplayAccess.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function grantReplayAccess(replayId: number, moderatorName: string): void { - BackendService.sendModeratorCommand('Command_GrantReplayAccess', { replayId, moderatorName }, { - onSuccess: () => { - ModeratorPersistence.grantReplayAccess(replayId, moderatorName); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/index.ts b/webclient/src/websocket/commands/moderator/index.ts deleted file mode 100644 index 10bb0e1c6..000000000 --- a/webclient/src/websocket/commands/moderator/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from './banFromServer'; -export * from './forceActivateUser'; -export * from './getAdminNotes'; -export * from './getBanHistory'; -export * from './getWarnHistory'; -export * from './getWarnList'; -export * from './grantReplayAccess'; -export * from './updateAdminNotes'; -export * from './viewLogHistory'; -export * from './warnUser'; diff --git a/webclient/src/websocket/commands/moderator/updateAdminNotes.ts b/webclient/src/websocket/commands/moderator/updateAdminNotes.ts deleted file mode 100644 index c7ac315c5..000000000 --- a/webclient/src/websocket/commands/moderator/updateAdminNotes.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function updateAdminNotes(userName: string, notes: string): void { - BackendService.sendModeratorCommand('Command_UpdateAdminNotes', { userName, notes }, { - onSuccess: () => { - ModeratorPersistence.updateAdminNotes(userName, notes); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/viewLogHistory.ts b/webclient/src/websocket/commands/moderator/viewLogHistory.ts deleted file mode 100644 index 19a930608..000000000 --- a/webclient/src/websocket/commands/moderator/viewLogHistory.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; -import { LogFilters } from 'types'; - -export function viewLogHistory(filters: LogFilters): void { - BackendService.sendModeratorCommand('Command_ViewLogHistory', filters, { - responseName: 'Response_ViewLogHistory', - onSuccess: (response) => { - ModeratorPersistence.viewLogs(response.logMessage); - }, - }); -} diff --git a/webclient/src/websocket/commands/moderator/warnUser.ts b/webclient/src/websocket/commands/moderator/warnUser.ts deleted file mode 100644 index 0e0271d4b..000000000 --- a/webclient/src/websocket/commands/moderator/warnUser.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { ModeratorPersistence } from '../../persistence'; - -export function warnUser(userName: string, reason: string, clientid?: string, removeMessages?: number): void { - BackendService.sendModeratorCommand('Command_WarnUser', { userName, reason, clientid, removeMessages }, { - onSuccess: () => { - ModeratorPersistence.warnUser(userName); - }, - }); -} diff --git a/webclient/src/websocket/commands/room/createGame.ts b/webclient/src/websocket/commands/room/createGame.ts deleted file mode 100644 index 62565e0e6..000000000 --- a/webclient/src/websocket/commands/room/createGame.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { RoomPersistence } from '../../persistence'; -import { GameConfig } from 'types'; - -export function createGame(roomId: number, gameConfig: GameConfig): void { - BackendService.sendRoomCommand(roomId, 'Command_CreateGame', gameConfig, { - onSuccess: () => { - RoomPersistence.gameCreated(roomId); - }, - }); -} diff --git a/webclient/src/websocket/commands/room/index.ts b/webclient/src/websocket/commands/room/index.ts deleted file mode 100644 index 18235618c..000000000 --- a/webclient/src/websocket/commands/room/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './createGame'; -export * from './joinGame'; -export * from './leaveRoom'; -export * from './roomSay'; diff --git a/webclient/src/websocket/commands/room/joinGame.ts b/webclient/src/websocket/commands/room/joinGame.ts deleted file mode 100644 index ef4b1fff2..000000000 --- a/webclient/src/websocket/commands/room/joinGame.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { RoomPersistence } from '../../persistence'; -import { JoinGameParams } from 'types'; - -export function joinGame(roomId: number, joinGameParams: JoinGameParams): void { - BackendService.sendRoomCommand(roomId, 'Command_JoinGame', joinGameParams, { - onSuccess: () => { - RoomPersistence.joinedGame(roomId, joinGameParams.gameId); - }, - }); -} diff --git a/webclient/src/websocket/commands/room/leaveRoom.ts b/webclient/src/websocket/commands/room/leaveRoom.ts deleted file mode 100644 index 7cd64a0e2..000000000 --- a/webclient/src/websocket/commands/room/leaveRoom.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { RoomPersistence } from '../../persistence'; - -export function leaveRoom(roomId: number): void { - BackendService.sendRoomCommand(roomId, 'Command_LeaveRoom', {}, { - onSuccess: () => { - RoomPersistence.leaveRoom(roomId); - }, - }); -} diff --git a/webclient/src/websocket/commands/room/roomSay.ts b/webclient/src/websocket/commands/room/roomSay.ts deleted file mode 100644 index a429845be..000000000 --- a/webclient/src/websocket/commands/room/roomSay.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; - -export function roomSay(roomId: number, message: string): void { - const trimmed = message.trim(); - - if (!trimmed) { - return; - } - - BackendService.sendRoomCommand(roomId, 'Command_RoomSay', { message: trimmed }, {}); -} diff --git a/webclient/src/websocket/commands/session/accountEdit.ts b/webclient/src/websocket/commands/session/accountEdit.ts deleted file mode 100644 index 31bf2d3f6..000000000 --- a/webclient/src/websocket/commands/session/accountEdit.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function accountEdit(passwordCheck: string, realName?: string, email?: string, country?: string): void { - BackendService.sendSessionCommand('Command_AccountEdit', { passwordCheck, realName, email, country }, { - onSuccess: () => { - SessionPersistence.accountEditChanged(realName, email, country); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/accountImage.ts b/webclient/src/websocket/commands/session/accountImage.ts deleted file mode 100644 index cd0e24403..000000000 --- a/webclient/src/websocket/commands/session/accountImage.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function accountImage(image: Uint8Array): void { - BackendService.sendSessionCommand('Command_AccountImage', { image }, { - onSuccess: () => { - SessionPersistence.accountImageChanged(image); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/accountPassword.ts b/webclient/src/websocket/commands/session/accountPassword.ts deleted file mode 100644 index 81c7a993b..000000000 --- a/webclient/src/websocket/commands/session/accountPassword.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function accountPassword(oldPassword: string, newPassword: string, hashedNewPassword: string): void { - BackendService.sendSessionCommand('Command_AccountPassword', { oldPassword, newPassword, hashedNewPassword }, { - onSuccess: () => { - SessionPersistence.accountPasswordChange(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/activate.ts b/webclient/src/websocket/commands/session/activate.ts deleted file mode 100644 index 4cd0e8c4e..000000000 --- a/webclient/src/websocket/commands/session/activate.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { AccountActivationParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { ProtoController } from '../../services/ProtoController'; -import { SessionPersistence } from '../../persistence'; - -import { disconnect, login, updateStatus } from './'; - -export function activate(options: WebSocketConnectOptions, passwordSalt?: string): void { - const { userName, token } = options as unknown as AccountActivationParams; - - BackendService.sendSessionCommand('Command_Activate', { - ...webClient.clientConfig, - userName, - token, - }, { - onResponseCode: { - [ProtoController.root.Response.ResponseCode.RespActivationAccepted]: () => { - SessionPersistence.accountActivationSuccess(); - login(options, passwordSalt); - }, - }, - onError: () => { - updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed'); - disconnect(); - SessionPersistence.accountActivationFailed(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/addToList.ts b/webclient/src/websocket/commands/session/addToList.ts deleted file mode 100644 index c5bc3c5f0..000000000 --- a/webclient/src/websocket/commands/session/addToList.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function addToBuddyList(userName: string): void { - addToList('buddy', userName); -} - -export function addToIgnoreList(userName: string): void { - addToList('ignore', userName); -} - -export function addToList(list: string, userName: string): void { - BackendService.sendSessionCommand('Command_AddToList', { list, userName }, { - onSuccess: () => { - SessionPersistence.addToList(list, userName); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/connect.ts b/webclient/src/websocket/commands/session/connect.ts deleted file mode 100644 index 5b660fab1..000000000 --- a/webclient/src/websocket/commands/session/connect.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types'; -import webClient from '../../WebClient'; -import { updateStatus } from './'; - -export function connect(options: WebSocketConnectOptions, reason: WebSocketConnectReason): void { - switch (reason) { - case WebSocketConnectReason.LOGIN: - case WebSocketConnectReason.REGISTER: - case WebSocketConnectReason.ACTIVATE_ACCOUNT: - case WebSocketConnectReason.PASSWORD_RESET_REQUEST: - case WebSocketConnectReason.PASSWORD_RESET_CHALLENGE: - case WebSocketConnectReason.PASSWORD_RESET: - updateStatus(StatusEnum.CONNECTING, 'Connecting...'); - break; - case WebSocketConnectReason.TEST_CONNECTION: - webClient.testConnect({ ...options }); - return; - default: - updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Attempt: ' + reason); - return; - } - - webClient.connect({ ...options, reason }); -} diff --git a/webclient/src/websocket/commands/session/deckDel.ts b/webclient/src/websocket/commands/session/deckDel.ts deleted file mode 100644 index 752ce78d5..000000000 --- a/webclient/src/websocket/commands/session/deckDel.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function deckDel(deckId: number): void { - BackendService.sendSessionCommand('Command_DeckDel', { deckId }, { - onSuccess: () => { - SessionPersistence.deleteServerDeck(deckId); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/deckDelDir.ts b/webclient/src/websocket/commands/session/deckDelDir.ts deleted file mode 100644 index df5bbc223..000000000 --- a/webclient/src/websocket/commands/session/deckDelDir.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function deckDelDir(path: string): void { - BackendService.sendSessionCommand('Command_DeckDelDir', { path }, { - onSuccess: () => { - SessionPersistence.deleteServerDeckDir(path); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/deckList.ts b/webclient/src/websocket/commands/session/deckList.ts deleted file mode 100644 index 3d5a3499a..000000000 --- a/webclient/src/websocket/commands/session/deckList.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function deckList(): void { - BackendService.sendSessionCommand('Command_DeckList', {}, { - responseName: 'Response_DeckList', - onSuccess: (response) => { - SessionPersistence.updateServerDecks(response); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/deckNewDir.ts b/webclient/src/websocket/commands/session/deckNewDir.ts deleted file mode 100644 index 85ab16afb..000000000 --- a/webclient/src/websocket/commands/session/deckNewDir.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function deckNewDir(path: string, dirName: string): void { - BackendService.sendSessionCommand('Command_DeckNewDir', { path, dirName }, { - onSuccess: () => { - SessionPersistence.createServerDeckDir(path, dirName); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/deckUpload.ts b/webclient/src/websocket/commands/session/deckUpload.ts deleted file mode 100644 index 2679c4e8e..000000000 --- a/webclient/src/websocket/commands/session/deckUpload.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function deckUpload(path: string, deckId: number, deckList: string): void { - BackendService.sendSessionCommand('Command_DeckUpload', { path, deckId, deckList }, { - responseName: 'Response_DeckUpload', - onSuccess: (response) => { - SessionPersistence.uploadServerDeck(path, response.newFile); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/disconnect.ts b/webclient/src/websocket/commands/session/disconnect.ts deleted file mode 100644 index 9fe567677..000000000 --- a/webclient/src/websocket/commands/session/disconnect.ts +++ /dev/null @@ -1,5 +0,0 @@ -import webClient from '../../WebClient'; - -export function disconnect(): void { - webClient.disconnect(); -} diff --git a/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts b/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts deleted file mode 100644 index 05af1ccf9..000000000 --- a/webclient/src/websocket/commands/session/forgotPasswordChallenge.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ForgotPasswordChallengeParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; -import { disconnect, updateStatus } from './'; - -export function forgotPasswordChallenge(options: WebSocketConnectOptions): void { - const { userName, email } = options as unknown as ForgotPasswordChallengeParams; - - BackendService.sendSessionCommand('Command_ForgotPasswordChallenge', { - ...webClient.clientConfig, - userName, - email, - }, { - onSuccess: () => { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPassword(); - disconnect(); - }, - onError: () => { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPasswordFailed(); - disconnect(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/forgotPasswordRequest.ts b/webclient/src/websocket/commands/session/forgotPasswordRequest.ts deleted file mode 100644 index 23d301450..000000000 --- a/webclient/src/websocket/commands/session/forgotPasswordRequest.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ForgotPasswordParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -import { disconnect, updateStatus } from './'; - -export function forgotPasswordRequest(options: WebSocketConnectOptions): void { - const { userName } = options as unknown as ForgotPasswordParams; - - BackendService.sendSessionCommand('Command_ForgotPasswordRequest', { - ...webClient.clientConfig, - userName, - }, { - responseName: 'Response_ForgotPasswordRequest', - onSuccess: (resp) => { - if (resp?.challengeEmail) { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPasswordChallenge(); - } else { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPassword(); - } - disconnect(); - }, - onError: () => { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPasswordFailed(); - disconnect(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/forgotPasswordReset.ts b/webclient/src/websocket/commands/session/forgotPasswordReset.ts deleted file mode 100644 index d9a775816..000000000 --- a/webclient/src/websocket/commands/session/forgotPasswordReset.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ForgotPasswordResetParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; -import { hashPassword } from '../../utils'; - -import { disconnect, updateStatus } from '.'; - -export function forgotPasswordReset(options: WebSocketConnectOptions, passwordSalt?: string): void { - const { userName, token, newPassword } = options as unknown as ForgotPasswordResetParams; - - const params: any = { - ...webClient.clientConfig, - userName, - token, - }; - - if (passwordSalt) { - params.hashedNewPassword = hashPassword(passwordSalt, newPassword); - } else { - params.newPassword = newPassword; - } - - BackendService.sendSessionCommand('Command_ForgotPasswordReset', params, { - onSuccess: () => { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPasswordSuccess(); - disconnect(); - }, - onError: () => { - updateStatus(StatusEnum.DISCONNECTED, null); - SessionPersistence.resetPasswordFailed(); - disconnect(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/getGamesOfUser.ts b/webclient/src/websocket/commands/session/getGamesOfUser.ts deleted file mode 100644 index 8fb8aeb5b..000000000 --- a/webclient/src/websocket/commands/session/getGamesOfUser.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function getGamesOfUser(userName: string): void { - BackendService.sendSessionCommand('Command_GetGamesOfUser', { userName }, { - responseName: 'Response_GetGamesOfUser', - onSuccess: (response) => { - SessionPersistence.getGamesOfUser(userName, response); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/getUserInfo.ts b/webclient/src/websocket/commands/session/getUserInfo.ts deleted file mode 100644 index 5b0f178ae..000000000 --- a/webclient/src/websocket/commands/session/getUserInfo.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function getUserInfo(userName: string): void { - BackendService.sendSessionCommand('Command_GetUserInfo', { userName }, { - responseName: 'Response_GetUserInfo', - onSuccess: (response) => { - SessionPersistence.getUserInfo(response.userInfo); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/index.ts b/webclient/src/websocket/commands/session/index.ts deleted file mode 100644 index 74d0d062c..000000000 --- a/webclient/src/websocket/commands/session/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -export * from './accountEdit'; -export * from './accountImage'; -export * from './accountPassword'; -export * from './activate'; -export * from './addToList'; -export * from './connect'; -export * from './deckDel'; -export * from './deckDelDir'; -export * from './deckList'; -export * from './deckNewDir'; -export * from './deckUpload'; -export * from './disconnect'; -export * from './forgotPasswordChallenge'; -export * from './forgotPasswordRequest'; -export * from './forgotPasswordReset'; -export * from './getGamesOfUser'; -export * from './getUserInfo'; -export * from './joinRoom'; -export * from './listRooms'; -export * from './listUsers'; -export * from './login'; -export * from './message'; -export * from './ping'; -export * from './register'; -export * from './removeFromList'; -export * from './replayDeleteMatch'; -export * from './replayList'; -export * from './replayModifyMatch'; -export * from './requestPasswordSalt'; -export * from './updateStatus'; diff --git a/webclient/src/websocket/commands/session/joinRoom.ts b/webclient/src/websocket/commands/session/joinRoom.ts deleted file mode 100644 index be79976a0..000000000 --- a/webclient/src/websocket/commands/session/joinRoom.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { RoomPersistence } from '../../persistence'; - -export function joinRoom(roomId: number): void { - BackendService.sendSessionCommand('Command_JoinRoom', { roomId }, { - responseName: 'Response_JoinRoom', - onSuccess: (response) => { - RoomPersistence.joinRoom(response.roomInfo); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/listRooms.ts b/webclient/src/websocket/commands/session/listRooms.ts deleted file mode 100644 index 367dada9b..000000000 --- a/webclient/src/websocket/commands/session/listRooms.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { BackendService } from '../../services/BackendService'; - -export function listRooms(): void { - BackendService.sendSessionCommand('Command_ListRooms', {}, {}); -} diff --git a/webclient/src/websocket/commands/session/listUsers.ts b/webclient/src/websocket/commands/session/listUsers.ts deleted file mode 100644 index 9b95c1344..000000000 --- a/webclient/src/websocket/commands/session/listUsers.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function listUsers(): void { - BackendService.sendSessionCommand('Command_ListUsers', {}, { - responseName: 'Response_ListUsers', - onSuccess: (response) => { - SessionPersistence.updateUsers(response.userList); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/login.ts b/webclient/src/websocket/commands/session/login.ts deleted file mode 100644 index 6f3ec5ef5..000000000 --- a/webclient/src/websocket/commands/session/login.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { StatusEnum, WebSocketConnectOptions } from 'types'; -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { ProtoController } from '../../services/ProtoController'; -import { hashPassword } from '../../utils'; -import { SessionPersistence } from '../../persistence'; - -import { - disconnect, - listUsers, - listRooms, - updateStatus, -} from './'; - -export function login(options: WebSocketConnectOptions, passwordSalt?: string): void { - const { userName, password, hashedPassword } = options; - - const loginConfig: any = { - ...webClient.clientConfig, - clientid: 'webatrice', - userName, - }; - - if (passwordSalt) { - loginConfig.hashedPassword = hashedPassword || hashPassword(passwordSalt, password); - } else { - loginConfig.password = password; - } - - const { ResponseCode } = ProtoController.root.Response; - - const onLoginError = (message: string, extra?: () => void) => { - updateStatus(StatusEnum.DISCONNECTED, message); - extra?.(); - SessionPersistence.loginFailed(); - disconnect(); - }; - - BackendService.sendSessionCommand('Command_Login', loginConfig, { - responseName: 'Response_Login', - onSuccess: (resp) => { - const { buddyList, ignoreList, userInfo } = resp; - - SessionPersistence.updateBuddyList(buddyList); - SessionPersistence.updateIgnoreList(ignoreList); - SessionPersistence.updateUser(userInfo); - SessionPersistence.loginSuccessful(loginConfig); - - listUsers(); - listRooms(); - - updateStatus(StatusEnum.LOGGED_IN, 'Logged in.'); - }, - onResponseCode: { - [ResponseCode.RespClientUpdateRequired]: () => - onLoginError('Login failed: missing features'), - [ResponseCode.RespWrongPassword]: () => - onLoginError('Login failed: incorrect username or password'), - [ResponseCode.RespUsernameInvalid]: () => - onLoginError('Login failed: incorrect username or password'), - [ResponseCode.RespWouldOverwriteOldSession]: () => - onLoginError('Login failed: duplicated user session'), - [ResponseCode.RespUserIsBanned]: () => - onLoginError('Login failed: banned user'), - [ResponseCode.RespRegistrationRequired]: () => - onLoginError('Login failed: registration required'), - [ResponseCode.RespClientIdRequired]: () => - onLoginError('Login failed: missing client ID'), - [ResponseCode.RespContextError]: () => - onLoginError('Login failed: server error'), - [ResponseCode.RespAccountNotActivated]: () => - onLoginError('Login failed: account not activated', - () => SessionPersistence.accountAwaitingActivation(options) - ), - }, - onError: (responseCode) => - onLoginError(`Login failed: unknown error: ${responseCode}`), - }); -} diff --git a/webclient/src/websocket/commands/session/message.ts b/webclient/src/websocket/commands/session/message.ts deleted file mode 100644 index 075fc3c4b..000000000 --- a/webclient/src/websocket/commands/session/message.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function message(userName: string, message: string): void { - BackendService.sendSessionCommand('Command_Message', { userName, message }, { - onSuccess: () => { - SessionPersistence.directMessageSent(userName, message); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/ping.ts b/webclient/src/websocket/commands/session/ping.ts deleted file mode 100644 index fea2784a2..000000000 --- a/webclient/src/websocket/commands/session/ping.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { BackendService } from '../../services/BackendService'; - -export function ping(pingReceived: Function): void { - BackendService.sendSessionCommand('Command_Ping', {}, { - onResponse: (raw) => pingReceived(raw), - }); -} diff --git a/webclient/src/websocket/commands/session/register.ts b/webclient/src/websocket/commands/session/register.ts deleted file mode 100644 index a25b85868..000000000 --- a/webclient/src/websocket/commands/session/register.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { ServerRegisterParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { ProtoController } from '../../services/ProtoController'; -import { SessionPersistence } from '../../persistence'; -import { hashPassword } from '../../utils'; - -import { login, disconnect, updateStatus } from './'; - -export function register(options: WebSocketConnectOptions, passwordSalt?: string): void { - const { userName, password, email, country, realName } = options as ServerRegisterParams; - - const params: any = { - ...webClient.clientConfig, - userName, - email, - country, - realName, - }; - - if (passwordSalt) { - params.hashedPassword = hashPassword(passwordSalt, password); - } else { - params.password = password; - } - - const { ResponseCode } = ProtoController.root.Response; - - const onRegistrationError = (action: () => void) => { - action(); - updateStatus(StatusEnum.DISCONNECTED, 'Registration failed'); - disconnect(); - }; - - BackendService.sendSessionCommand('Command_Register', params, { - onResponseCode: { - [ResponseCode.RespRegistrationAccepted]: () => { - login(options, passwordSalt); - SessionPersistence.registrationSuccess(); - }, - [ResponseCode.RespRegistrationAcceptedNeedsActivation]: () => { - updateStatus(StatusEnum.DISCONNECTED, 'Registration accepted, awaiting activation'); - SessionPersistence.accountAwaitingActivation(options); - disconnect(); - }, - [ResponseCode.RespUserAlreadyExists]: () => onRegistrationError( - () => SessionPersistence.registrationUserNameError('Username is taken') - ), - [ResponseCode.RespUsernameInvalid]: () => onRegistrationError( - () => SessionPersistence.registrationUserNameError('Invalid username') - ), - [ResponseCode.RespPasswordTooShort]: () => onRegistrationError( - () => SessionPersistence.registrationPasswordError('Your password was too short') - ), - [ResponseCode.RespEmailRequiredToRegister]: () => onRegistrationError( - () => SessionPersistence.registrationRequiresEmail() - ), - [ResponseCode.RespEmailBlackListed]: () => onRegistrationError( - () => SessionPersistence.registrationEmailError('This email provider has been blocked') - ), - [ResponseCode.RespTooManyRequests]: () => onRegistrationError( - () => SessionPersistence.registrationEmailError('Max accounts reached for this email') - ), - [ResponseCode.RespRegistrationDisabled]: () => onRegistrationError( - () => SessionPersistence.registrationFailed('Registration is currently disabled') - ), - [ResponseCode.RespUserIsBanned]: (raw) => onRegistrationError( - () => SessionPersistence.registrationFailed(raw.reasonStr, raw.endTime) - ), - }, - onError: () => onRegistrationError( - () => SessionPersistence.registrationFailed('Registration failed due to a server issue') - ), - }); -} diff --git a/webclient/src/websocket/commands/session/removeFromList.ts b/webclient/src/websocket/commands/session/removeFromList.ts deleted file mode 100644 index aede49c49..000000000 --- a/webclient/src/websocket/commands/session/removeFromList.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function removeFromBuddyList(userName: string): void { - removeFromList('buddy', userName); -} - -export function removeFromIgnoreList(userName: string): void { - removeFromList('ignore', userName); -} - -export function removeFromList(list: string, userName: string): void { - BackendService.sendSessionCommand('Command_RemoveFromList', { list, userName }, { - onSuccess: () => { - SessionPersistence.removeFromList(list, userName); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/replayDeleteMatch.ts b/webclient/src/websocket/commands/session/replayDeleteMatch.ts deleted file mode 100644 index 24ac48f1c..000000000 --- a/webclient/src/websocket/commands/session/replayDeleteMatch.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function replayDeleteMatch(gameId: number): void { - BackendService.sendSessionCommand('Command_ReplayDeleteMatch', { gameId }, { - onSuccess: () => { - SessionPersistence.replayDeleteMatch(gameId); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/replayList.ts b/webclient/src/websocket/commands/session/replayList.ts deleted file mode 100644 index f39eb279f..000000000 --- a/webclient/src/websocket/commands/session/replayList.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function replayList(): void { - BackendService.sendSessionCommand('Command_ReplayList', {}, { - responseName: 'Response_ReplayList', - onSuccess: (response) => { - SessionPersistence.replayList(response.matchList); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/replayModifyMatch.ts b/webclient/src/websocket/commands/session/replayModifyMatch.ts deleted file mode 100644 index 9825047f3..000000000 --- a/webclient/src/websocket/commands/session/replayModifyMatch.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BackendService } from '../../services/BackendService'; -import { SessionPersistence } from '../../persistence'; - -export function replayModifyMatch(gameId: number, doNotHide: boolean): void { - BackendService.sendSessionCommand('Command_ReplayModifyMatch', { gameId, doNotHide }, { - onSuccess: () => { - SessionPersistence.replayModifyMatch(gameId, doNotHide); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/requestPasswordSalt.ts b/webclient/src/websocket/commands/session/requestPasswordSalt.ts deleted file mode 100644 index a3d1fc05c..000000000 --- a/webclient/src/websocket/commands/session/requestPasswordSalt.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { RequestPasswordSaltParams } from 'store'; -import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types'; - -import webClient from '../../WebClient'; -import { BackendService } from '../../services/BackendService'; -import { ProtoController } from '../../services/ProtoController'; -import { SessionPersistence } from '../../persistence'; - -import { - activate, - disconnect, - login, - forgotPasswordReset, - updateStatus -} from './'; - -export function requestPasswordSalt(options: WebSocketConnectOptions): void { - const { userName } = options as RequestPasswordSaltParams; - - const onFailure = () => { - switch (options.reason) { - case WebSocketConnectReason.ACTIVATE_ACCOUNT: - SessionPersistence.accountActivationFailed(); - break; - case WebSocketConnectReason.PASSWORD_RESET: - SessionPersistence.resetPasswordFailed(); - break; - default: - SessionPersistence.loginFailed(); - } - disconnect(); - }; - - BackendService.sendSessionCommand('Command_RequestPasswordSalt', { - ...webClient.clientConfig, - userName, - }, { - responseName: 'Response_PasswordSalt', - onSuccess: (resp) => { - const passwordSalt = resp?.passwordSalt; - - switch (options.reason) { - case WebSocketConnectReason.ACTIVATE_ACCOUNT: - activate(options, passwordSalt); - break; - case WebSocketConnectReason.PASSWORD_RESET: - forgotPasswordReset(options, passwordSalt); - break; - default: - login(options, passwordSalt); - } - }, - onResponseCode: { - [ProtoController.root.Response.ResponseCode.RespRegistrationRequired]: () => { - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: registration required'); - onFailure(); - }, - }, - onError: () => { - updateStatus(StatusEnum.DISCONNECTED, 'Login failed: Unknown Reason'); - onFailure(); - }, - }); -} diff --git a/webclient/src/websocket/commands/session/updateStatus.ts b/webclient/src/websocket/commands/session/updateStatus.ts deleted file mode 100644 index eddd774f8..000000000 --- a/webclient/src/websocket/commands/session/updateStatus.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { StatusEnum } from 'types'; -import webClient from '../../WebClient'; -import { SessionPersistence } from '../../persistence'; - -export function updateStatus(status: StatusEnum, description: string): void { - SessionPersistence.updateStatus(status, description); - - webClient.updateStatus(status); -} diff --git a/webclient/src/websocket/events/common/index.ts b/webclient/src/websocket/events/common/index.ts deleted file mode 100644 index 77a325c1e..000000000 --- a/webclient/src/websocket/events/common/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ProtobufEvents } from '../../services/ProtobufService'; -import { playerPropertiesChanged } from './playerPropertiesChanged'; - -export const CommonEvents: ProtobufEvents = { - '.Event_PlayerPropertiesChanged.ext': playerPropertiesChanged, -} diff --git a/webclient/src/websocket/events/common/playerPropertiesChanged.ts b/webclient/src/websocket/events/common/playerPropertiesChanged.ts deleted file mode 100644 index 557e57ac9..000000000 --- a/webclient/src/websocket/events/common/playerPropertiesChanged.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PlayerGamePropertiesData } from '../session/interfaces'; -import { SessionPersistence } from '../../persistence'; - -export function playerPropertiesChanged(payload: PlayerGamePropertiesData): void { - SessionPersistence.playerPropertiesChanged(payload); -} diff --git a/webclient/src/websocket/events/game/index.ts b/webclient/src/websocket/events/game/index.ts deleted file mode 100644 index a7b3277a5..000000000 --- a/webclient/src/websocket/events/game/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ProtobufEvents } from '../../services/ProtobufService'; -import { joinGame } from './joinGame'; -import { leaveGame } from './leaveGame'; - - -export const GameEvents: ProtobufEvents = { - '.Event_Join.ext': joinGame, - '.Event_Leave.ext': leaveGame, - '.Event_GameClosed.ext': () => console.log('Event_GameClosed.ext'), - '.Event_GameHostChanged.ext': () => console.log('Event_GameHostChanged.ext'), - '.Event_Kicked.ext': () => console.log('Event_Kicked.ext'), - '.Event_GameStateChanged.ext': () => console.log('Event_GameStateChanged.ext'), - // '.Event_PlayerPropertiesChanged.ext': () => console.log("Event_PlayerProperties.ext"), - '.Event_GameSay.ext': () => console.log('Event_GameSay.ext'), - '.Event_CreateArrow.ext': () => console.log('Event_CreateArrow.ext'), - '.Event_DeleteArrow.ext': () => console.log('Event_DeleteArrow.ext'), - '.Event_CreateCounter.ext': () => console.log('Event_CreateCounter.ext'), - '.Event_SetCounter.ext': () => console.log('Event_SetCounter.ext'), - '.Event_DelCounter.ext': () => console.log('Event_DelCounter.ext'), - '.Event_DrawCards.ext': () => console.log('Event_DrawCards.ext'), - '.Event_RevealCards.ext': () => console.log('Event_RevealCards.ext'), - '.Event_Shuffle.ext': () => console.log('Event_Shuffle.ext'), - '.Event_RollDie.ext': () => console.log('Event_Roll.ext'), - '.Event_MoveCard.ext': () => console.log('Event_MoveCard.ext'), - '.Event_FlipCard.ext': () => console.log('Event_FlipCard.ext'), - '.Event_DestroyCard.ext': () => console.log('Event_DestroyCard.ext'), - '.Event_AttachCard.ext': () => console.log('Event_AttachCard.ext'), - '.Event_CreateToken.ext': () => console.log('Event_CreateToken.ext'), - '.Event_SetCardAttribute.ext': () => console.log('Event_SetCardAttribute.ext'), - '.Event_SetCardCounter.ext': () => console.log('Event_SetCardCounter.ext'), - '.Event_SetActivePlayer.ext': () => console.log('Event_SetActivePlayer.ext'), - '.Event_SetActivePhase.ext': () => console.log('Event_SetActivePhase.ext'), - '.Event_DumpZone.ext': () => console.log('Event_DumpZone.ext'), - '.Event_ChangeZoneProperties.ext': () => console.log('Event_ChangeZoneProperties.ext'), - '.Event_ReverseTurn.ext': () => console.log('Event_ReverseTurn.ext'), -}; diff --git a/webclient/src/websocket/events/game/joinGame.ts b/webclient/src/websocket/events/game/joinGame.ts deleted file mode 100644 index 30cef87dc..000000000 --- a/webclient/src/websocket/events/game/joinGame.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { GamePersistence } from '../../persistence'; -import { PlayerGamePropertiesData } from '../session/interfaces'; - -export function joinGame(playerGamePropertiesData: PlayerGamePropertiesData): void { - GamePersistence.joinGame(playerGamePropertiesData); -} diff --git a/webclient/src/websocket/events/game/leaveGame.ts b/webclient/src/websocket/events/game/leaveGame.ts deleted file mode 100644 index 74a4dc6c5..000000000 --- a/webclient/src/websocket/events/game/leaveGame.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { LeaveGameReason } from 'types'; -import { GamePersistence } from '../../persistence'; - - -export function leaveGame(reason: LeaveGameReason): void { - GamePersistence.leaveGame(reason); -} diff --git a/webclient/src/websocket/events/index.ts b/webclient/src/websocket/events/index.ts deleted file mode 100644 index ed74f9b93..000000000 --- a/webclient/src/websocket/events/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './common'; -export * from './room'; -export * from './session'; -export * from './game'; diff --git a/webclient/src/websocket/events/room/index.ts b/webclient/src/websocket/events/room/index.ts deleted file mode 100644 index 5b571d388..000000000 --- a/webclient/src/websocket/events/room/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ProtobufEvents } from '../../services/ProtobufService'; - -import { joinRoom } from './joinRoom'; -import { leaveRoom } from './leaveRoom'; -import { listGames } from './listGames'; -import { roomSay } from './roomSay'; -import { removeMessages } from './removeMessages'; - -export const RoomEvents: ProtobufEvents = { - '.Event_JoinRoom.ext': joinRoom, - '.Event_LeaveRoom.ext': leaveRoom, - '.Event_ListGames.ext': listGames, - '.Event_RemoveMessages.ext': removeMessages, - '.Event_RoomSay.ext': roomSay, -}; diff --git a/webclient/src/websocket/events/room/interfaces.ts b/webclient/src/websocket/events/room/interfaces.ts deleted file mode 100644 index b3f922141..000000000 --- a/webclient/src/websocket/events/room/interfaces.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Game, User } from 'types'; - -export interface JoinRoomData { - userInfo: User; -} - -export interface LeaveRoomData { - name: string; -} - -export interface ListGamesData { - gameList: Game[]; -} - -export interface RemoveMessagesData { - name: string; - amount: number; -} - -export interface RoomEvent { - roomEvent: { - roomId: number; - } -} diff --git a/webclient/src/websocket/events/room/joinRoom.ts b/webclient/src/websocket/events/room/joinRoom.ts deleted file mode 100644 index b1a5f6606..000000000 --- a/webclient/src/websocket/events/room/joinRoom.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { RoomPersistence } from '../../persistence'; -import { JoinRoomData, RoomEvent } from './interfaces'; - -export function joinRoom({ userInfo }: JoinRoomData, { roomEvent: { roomId } }: RoomEvent): void { - RoomPersistence.userJoined(roomId, userInfo); -} diff --git a/webclient/src/websocket/events/room/leaveRoom.ts b/webclient/src/websocket/events/room/leaveRoom.ts deleted file mode 100644 index 6d45197fc..000000000 --- a/webclient/src/websocket/events/room/leaveRoom.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { RoomPersistence } from '../../persistence'; -import { LeaveRoomData, RoomEvent } from './interfaces'; - -export function leaveRoom({ name }: LeaveRoomData, { roomEvent: { roomId } }: RoomEvent): void { - RoomPersistence.userLeft(roomId, name); -} diff --git a/webclient/src/websocket/events/room/listGames.ts b/webclient/src/websocket/events/room/listGames.ts deleted file mode 100644 index d460a5336..000000000 --- a/webclient/src/websocket/events/room/listGames.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { RoomPersistence } from '../../persistence'; -import { ListGamesData, RoomEvent } from './interfaces'; - -export function listGames({ gameList }: ListGamesData, { roomEvent: { roomId } }: RoomEvent): void { - RoomPersistence.updateGames(roomId, gameList); -} diff --git a/webclient/src/websocket/events/room/removeMessages.ts b/webclient/src/websocket/events/room/removeMessages.ts deleted file mode 100644 index 4fe01cb6f..000000000 --- a/webclient/src/websocket/events/room/removeMessages.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { RoomPersistence } from '../../persistence'; -import { RemoveMessagesData, RoomEvent } from './interfaces'; - -export function removeMessages({ name, amount }: RemoveMessagesData, { roomEvent: { roomId } }: RoomEvent): void { - RoomPersistence.removeMessages(roomId, name, amount); -} diff --git a/webclient/src/websocket/events/room/roomSay.ts b/webclient/src/websocket/events/room/roomSay.ts deleted file mode 100644 index 5a96198ea..000000000 --- a/webclient/src/websocket/events/room/roomSay.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Message } from 'types'; - -import { RoomPersistence } from '../../persistence'; -import { RoomEvent } from './interfaces'; - -export function roomSay(message: Message, { roomEvent: { roomId } }: RoomEvent): void { - RoomPersistence.addMessage(roomId, message); -} diff --git a/webclient/src/websocket/events/session/addToList.ts b/webclient/src/websocket/events/session/addToList.ts deleted file mode 100644 index 08b19b45d..000000000 --- a/webclient/src/websocket/events/session/addToList.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { AddToListData } from './interfaces'; - -export function addToList({ listName, userInfo }: AddToListData): void { - switch (listName) { - case 'buddy': { - SessionPersistence.addToBuddyList(userInfo); - break; - } - case 'ignore': { - SessionPersistence.addToIgnoreList(userInfo); - break; - } - default: { - console.log(`Attempted to add to unknown list: ${listName}`); - } - } -} diff --git a/webclient/src/websocket/events/session/connectionClosed.ts b/webclient/src/websocket/events/session/connectionClosed.ts deleted file mode 100644 index 227113059..000000000 --- a/webclient/src/websocket/events/session/connectionClosed.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { StatusEnum } from 'types'; -import { ProtoController } from '../../services/ProtoController'; -import { updateStatus } from '../../commands/session'; -import { ConnectionClosedData } from './interfaces'; - -export function connectionClosed({ reason, reasonStr }: ConnectionClosedData): void { - let message: string; - - // @TODO (5) - if (reasonStr) { - message = reasonStr; - } else { - const { CloseReason } = ProtoController.root.Event_ConnectionClosed; - switch (reason) { - case CloseReason.USER_LIMIT_REACHED: - message = 'The server has reached its maximum user capacity'; - break; - case CloseReason.TOO_MANY_CONNECTIONS: - message = 'There are too many concurrent connections from your address'; - break; - case CloseReason.BANNED: - message = 'You are banned'; - break; - case CloseReason.DEMOTED: - message = 'You were demoted'; - break; - case CloseReason.SERVER_SHUTDOWN: - message = 'Scheduled server shutdown'; - break; - case CloseReason.USERNAMEINVALID: - message = 'Invalid username'; - break; - case CloseReason.LOGGEDINELSEWERE: - message = 'You have been logged out due to logging in at another location'; - break; - case CloseReason.OTHER: - default: - message = 'Unknown reason'; - break; - } - } - - updateStatus(StatusEnum.DISCONNECTED, message); -} diff --git a/webclient/src/websocket/events/session/gameJoined.ts b/webclient/src/websocket/events/session/gameJoined.ts deleted file mode 100644 index 8c0d49006..000000000 --- a/webclient/src/websocket/events/session/gameJoined.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { GameJoinedData } from './interfaces'; - -export function gameJoined(gameJoined: GameJoinedData): void { - SessionPersistence.gameJoined(gameJoined); -} diff --git a/webclient/src/websocket/events/session/index.ts b/webclient/src/websocket/events/session/index.ts deleted file mode 100644 index 5b3ab198e..000000000 --- a/webclient/src/websocket/events/session/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ProtobufEvents } from '../../services/ProtobufService'; -import { addToList } from './addToList'; -import { connectionClosed } from './connectionClosed'; -import { listRooms } from './listRooms'; -import { notifyUser } from './notifyUser'; -import { removeFromList } from './removeFromList'; -import { replayAdded } from './replayAdded'; -import { serverCompleteList } from './serverCompleteList'; -import { serverIdentification } from './serverIdentification'; -import { serverMessage } from './serverMessage'; -import { serverShutdown } from './serverShutdown'; -import { userJoined } from './userJoined'; -import { userLeft } from './userLeft'; -import { userMessage } from './userMessage'; -import { gameJoined } from './gameJoined'; - -export const SessionEvents: ProtobufEvents = { - '.Event_AddToList.ext': addToList, - '.Event_ConnectionClosed.ext': connectionClosed, - '.Event_GameJoined.ext': gameJoined, - '.Event_ListRooms.ext': listRooms, - '.Event_NotifyUser.ext': notifyUser, - '.Event_RemoveFromList.ext': removeFromList, - '.Event_ReplayAdded.ext': replayAdded, - '.Event_ServerCompleteList.ext': serverCompleteList, - '.Event_ServerIdentification.ext': serverIdentification, - '.Event_ServerMessage.ext': serverMessage, - '.Event_ServerShutdown.ext': serverShutdown, - '.Event_UserJoined.ext': userJoined, - '.Event_UserLeft.ext': userLeft, - '.Event_UserMessage.ext': userMessage, -} diff --git a/webclient/src/websocket/events/session/interfaces.ts b/webclient/src/websocket/events/session/interfaces.ts deleted file mode 100644 index ddc10d103..000000000 --- a/webclient/src/websocket/events/session/interfaces.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Game, NotificationType, ReplayMatch, Room, User } from 'types'; - -export interface AddToListData { - listName: string; - userInfo: User; -} - -export interface ConnectionClosedData { - endTime: number; - reason: number; - reasonStr: string; -} - -export interface GameJoinedData { - gameInfo: Game; - gameTypes: any[]; - hostId: number; - playerId: number; - spectator: boolean; - resuming: boolean; - judge: boolean; -} - -export interface ListRoomsData { - roomList: Room[]; -} - -export interface NotifyUserData { - type: NotificationType; - warningReason: string; - customTitle: string; - customContent: string; -} - -export interface PlayerGamePropertiesData { - playerId: number; - userInfo: User; - spectator: boolean; - conceded: boolean; - readyStart: boolean; - deckHash: string; - pingSeconds: number; - sideboardLocked: boolean; - judge: boolean; -} - -export interface RemoveFromListData { - listName: string; - userName: string; -} - -export interface ServerIdentificationData { - protocolVersion: number; - serverName: string; - serverVersion: string; - serverOptions: number; -} - -export interface ServerMessageData { - message: string; -} - -export interface ServerShutdownData { - reason: string; - minutes: number; -} - -export interface UserJoinedData { - userInfo: User; -} - -export interface UserLeftData { - name: string; -} - -export interface UserMessageData { - senderName: string; - receiverName: string; - message: string; -} - -export interface ReplayAddedData { - matchInfo: ReplayMatch; -} - -export interface ServerCompleteListData { - serverId: number; - userList: User[]; - roomList: Room[]; -} diff --git a/webclient/src/websocket/events/session/listRooms.ts b/webclient/src/websocket/events/session/listRooms.ts deleted file mode 100644 index faba2968e..000000000 --- a/webclient/src/websocket/events/session/listRooms.ts +++ /dev/null @@ -1,16 +0,0 @@ -import webClient from '../../WebClient'; -import { joinRoom } from '../../commands/session'; -import { RoomPersistence } from '../../persistence'; -import { ListRoomsData } from './interfaces'; - -export function listRooms({ roomList }: ListRoomsData): void { - RoomPersistence.updateRooms(roomList); - - if (webClient.clientOptions.autojoinrooms) { - roomList.forEach(({ autoJoin, roomId }) => { - if (autoJoin) { - joinRoom(roomId); - } - }); - } -} diff --git a/webclient/src/websocket/events/session/notifyUser.ts b/webclient/src/websocket/events/session/notifyUser.ts deleted file mode 100644 index f5673fe3f..000000000 --- a/webclient/src/websocket/events/session/notifyUser.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { NotifyUserData } from './interfaces'; - - -export function notifyUser(payload: NotifyUserData): void { - SessionPersistence.notifyUser(payload); -} diff --git a/webclient/src/websocket/events/session/removeFromList.ts b/webclient/src/websocket/events/session/removeFromList.ts deleted file mode 100644 index 20e2d7f54..000000000 --- a/webclient/src/websocket/events/session/removeFromList.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { RemoveFromListData } from './interfaces'; - -export function removeFromList({ listName, userName }: RemoveFromListData): void { - switch (listName) { - case 'buddy': { - SessionPersistence.removeFromBuddyList(userName); - break; - } - case 'ignore': { - SessionPersistence.removeFromIgnoreList(userName); - break; - } - default: { - console.log(`Attempted to remove from unknown list: ${listName}`); - } - } -} diff --git a/webclient/src/websocket/events/session/replayAdded.ts b/webclient/src/websocket/events/session/replayAdded.ts deleted file mode 100644 index 18a4ea82d..000000000 --- a/webclient/src/websocket/events/session/replayAdded.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { ReplayAddedData } from './interfaces'; - -export function replayAdded({ matchInfo }: ReplayAddedData): void { - SessionPersistence.replayAdded(matchInfo); -} diff --git a/webclient/src/websocket/events/session/serverCompleteList.ts b/webclient/src/websocket/events/session/serverCompleteList.ts deleted file mode 100644 index 77d37a31e..000000000 --- a/webclient/src/websocket/events/session/serverCompleteList.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RoomPersistence, SessionPersistence } from '../../persistence'; -import { ServerCompleteListData } from './interfaces'; - -export function serverCompleteList({ userList, roomList }: ServerCompleteListData): void { - SessionPersistence.updateUsers(userList); - RoomPersistence.updateRooms(roomList); -} diff --git a/webclient/src/websocket/events/session/serverIdentification.ts b/webclient/src/websocket/events/session/serverIdentification.ts deleted file mode 100644 index 87ae79453..000000000 --- a/webclient/src/websocket/events/session/serverIdentification.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { StatusEnum, WebSocketConnectOptions, WebSocketConnectReason } from 'types'; - -import webClient from '../../WebClient'; -import { - activate, - disconnect, - login, - register, - requestPasswordSalt, - forgotPasswordChallenge, - forgotPasswordRequest, - forgotPasswordReset, - updateStatus, -} from '../../commands/session'; -import { generateSalt, passwordSaltSupported } from '../../utils'; -import { ServerIdentificationData } from './interfaces'; -import { SessionPersistence } from '../../persistence'; - -export function serverIdentification(info: ServerIdentificationData): void { - const { serverName, serverVersion, protocolVersion, serverOptions } = info; - if (protocolVersion !== webClient.protocolVersion) { - updateStatus(StatusEnum.DISCONNECTED, `Protocol version mismatch: ${protocolVersion}`); - disconnect(); - return; - } - - const getPasswordSalt = passwordSaltSupported(serverOptions); - const connectOptions = { ...webClient.options }; - - switch (connectOptions.reason) { - case WebSocketConnectReason.LOGIN: - updateStatus(StatusEnum.LOGGING_IN, 'Logging In...'); - if (getPasswordSalt) { - requestPasswordSalt(connectOptions); - } else { - login(connectOptions); - } - break; - case WebSocketConnectReason.REGISTER: - const passwordSalt = getPasswordSalt ? generateSalt() : null; - register(connectOptions, passwordSalt); - break; - case WebSocketConnectReason.ACTIVATE_ACCOUNT: - if (getPasswordSalt) { - requestPasswordSalt(connectOptions); - } else { - activate(connectOptions); - } - break; - case WebSocketConnectReason.PASSWORD_RESET_REQUEST: - forgotPasswordRequest(connectOptions); - break; - case WebSocketConnectReason.PASSWORD_RESET_CHALLENGE: - forgotPasswordChallenge(connectOptions); - break; - case WebSocketConnectReason.PASSWORD_RESET: - if (getPasswordSalt) { - requestPasswordSalt(connectOptions); - } else { - forgotPasswordReset(connectOptions); - } - break; - default: - updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Reason: ' + connectOptions.reason); - disconnect(); - break; - } - - webClient.options = {} as WebSocketConnectOptions; - SessionPersistence.updateInfo(serverName, serverVersion); -} diff --git a/webclient/src/websocket/events/session/serverMessage.ts b/webclient/src/websocket/events/session/serverMessage.ts deleted file mode 100644 index f9e52aa7a..000000000 --- a/webclient/src/websocket/events/session/serverMessage.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { ServerMessageData } from './interfaces'; - -export function serverMessage({ message }: ServerMessageData): void { - SessionPersistence.serverMessage(message); -} diff --git a/webclient/src/websocket/events/session/serverShutdown.ts b/webclient/src/websocket/events/session/serverShutdown.ts deleted file mode 100644 index cdda893a4..000000000 --- a/webclient/src/websocket/events/session/serverShutdown.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { ServerShutdownData } from './interfaces'; - - -export function serverShutdown(payload: ServerShutdownData): void { - SessionPersistence.serverShutdown(payload); -} diff --git a/webclient/src/websocket/events/session/userJoined.ts b/webclient/src/websocket/events/session/userJoined.ts deleted file mode 100644 index cb512db60..000000000 --- a/webclient/src/websocket/events/session/userJoined.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { UserJoinedData } from './interfaces'; - -export function userJoined({ userInfo }: UserJoinedData): void { - SessionPersistence.userJoined(userInfo); -} diff --git a/webclient/src/websocket/events/session/userLeft.ts b/webclient/src/websocket/events/session/userLeft.ts deleted file mode 100644 index 9e00e59e1..000000000 --- a/webclient/src/websocket/events/session/userLeft.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { UserLeftData } from './interfaces'; - -export function userLeft({ name }: UserLeftData): void { - SessionPersistence.userLeft(name); -} diff --git a/webclient/src/websocket/events/session/userMessage.ts b/webclient/src/websocket/events/session/userMessage.ts deleted file mode 100644 index bff08460b..000000000 --- a/webclient/src/websocket/events/session/userMessage.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { SessionPersistence } from '../../persistence'; -import { UserMessageData } from './interfaces'; - - - -export function userMessage(payload: UserMessageData): void { - SessionPersistence.userMessage(payload); -} diff --git a/webclient/src/websocket/index.ts b/webclient/src/websocket/index.ts deleted file mode 100644 index b52c97168..000000000 --- a/webclient/src/websocket/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './commands'; - -export { default as webClient } from './WebClient'; diff --git a/webclient/src/websocket/persistence/AdminPersistence.ts b/webclient/src/websocket/persistence/AdminPersistence.ts deleted file mode 100644 index 9552d8abf..000000000 --- a/webclient/src/websocket/persistence/AdminPersistence.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ServerDispatch } from 'store'; - -export class AdminPersistence { - static adjustMod(userName: string, shouldBeMod: boolean, shouldBeJudge: boolean) { - ServerDispatch.adjustMod(userName, shouldBeMod, shouldBeJudge) - } - - static reloadConfig() { - ServerDispatch.reloadConfig(); - } - - static shutdownServer() { - ServerDispatch.shutdownServer(); - } - - static updateServerMessage() { - ServerDispatch.updateServerMessage(); - } -} diff --git a/webclient/src/websocket/persistence/GamePersistence.ts b/webclient/src/websocket/persistence/GamePersistence.ts deleted file mode 100644 index e39c1b5a9..000000000 --- a/webclient/src/websocket/persistence/GamePersistence.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { PlayerGamePropertiesData } from '../events/session/interfaces'; -import { LeaveGameReason } from '../../types'; - -export class GamePersistence { - static joinGame(playerGamePropertiesData: PlayerGamePropertiesData) { - console.log('joinGame', playerGamePropertiesData); - } - - static leaveGame(reason: LeaveGameReason) { - console.log('leaveGame', reason); - } -} diff --git a/webclient/src/websocket/persistence/ModeratorPersistence.ts b/webclient/src/websocket/persistence/ModeratorPersistence.ts deleted file mode 100644 index d1b991fa0..000000000 --- a/webclient/src/websocket/persistence/ModeratorPersistence.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ServerDispatch } from 'store'; -import { BanHistoryItem, LogItem, WarnHistoryItem, WarnListItem } from 'types'; - -import NormalizeService from '../utils/NormalizeService'; - -export class ModeratorPersistence { - static banFromServer(userName: string): void { - ServerDispatch.banFromServer(userName); - } - - static banHistory(userName: string, banHistory: BanHistoryItem[]): void { - ServerDispatch.banHistory(userName, banHistory); - } - - static viewLogs(logs: LogItem[]): void { - ServerDispatch.viewLogs(NormalizeService.normalizeLogs(logs)); - } - - static warnHistory(userName: string, warnHistory: WarnHistoryItem[]): void { - ServerDispatch.warnHistory(userName, warnHistory); - } - - static warnListOptions(warnList: WarnListItem[]): void { - ServerDispatch.warnListOptions(warnList); - } - - static warnUser(userName: string): void { - ServerDispatch.warnUser(userName); - } - - static grantReplayAccess(replayId: number, moderatorName: string): void { - ServerDispatch.grantReplayAccess(replayId, moderatorName); - } - - static forceActivateUser(usernameToActivate: string, moderatorName: string): void { - ServerDispatch.forceActivateUser(usernameToActivate, moderatorName); - } - - static getAdminNotes(userName: string, notes: string): void { - ServerDispatch.getAdminNotes(userName, notes); - } - - static updateAdminNotes(userName: string, notes: string): void { - ServerDispatch.updateAdminNotes(userName, notes); - } -} diff --git a/webclient/src/websocket/persistence/RoomPersistence.ts b/webclient/src/websocket/persistence/RoomPersistence.ts deleted file mode 100644 index a69e79338..000000000 --- a/webclient/src/websocket/persistence/RoomPersistence.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { store, RoomsDispatch, RoomsSelectors } from 'store'; -import { Game, Message, Room, User } from 'types'; -import NormalizeService from '../utils/NormalizeService'; - -export class RoomPersistence { - static clearStore() { - RoomsDispatch.clearStore(); - } - - static joinRoom(roomInfo: Room) { - NormalizeService.normalizeRoomInfo(roomInfo); - RoomsDispatch.joinRoom(roomInfo); - } - - static leaveRoom(roomId: number) { - RoomsDispatch.leaveRoom(roomId); - } - - static updateRooms(rooms: Room[]) { - RoomsDispatch.updateRooms(rooms); - } - - static updateGames(roomId: number, gameList: Game[]) { - const game = gameList[0]; - - if (!game.gameType) { - const room = RoomsSelectors.getRoom(store.getState(), roomId); - - if (room) { - const { gametypeMap } = room; - NormalizeService.normalizeGameObject(game, gametypeMap); - } - } - - RoomsDispatch.updateGames(roomId, gameList); - } - - static addMessage(roomId: number, message: Message) { - NormalizeService.normalizeUserMessage(message); - - RoomsDispatch.addMessage(roomId, message); - } - - static userJoined(roomId: number, user: User) { - RoomsDispatch.userJoined(roomId, user); - } - - static userLeft(roomId: number, name: string) { - RoomsDispatch.userLeft(roomId, name); - } - - static removeMessages(roomId: number, name: string, amount: number): void { - RoomsDispatch.removeMessages(roomId, name, amount); - }; - - static gameCreated(roomId: number) { - RoomsDispatch.gameCreated(roomId); - } - - static joinedGame(roomId: number, gameId: number) { - RoomsDispatch.joinedGame(roomId, gameId); - } -} diff --git a/webclient/src/websocket/persistence/SessionPersistence.ts b/webclient/src/websocket/persistence/SessionPersistence.ts deleted file mode 100644 index 9962af968..000000000 --- a/webclient/src/websocket/persistence/SessionPersistence.ts +++ /dev/null @@ -1,247 +0,0 @@ -import { ServerDispatch } from 'store'; -import { DeckList, DeckStorageTreeItem, ReplayMatch, StatusEnum, User, WebSocketConnectOptions } from 'types'; - -import { sanitizeHtml } from 'websocket/utils'; -import { - GameJoinedData, - NotifyUserData, - PlayerGamePropertiesData, - ServerShutdownData, - UserMessageData -} from '../events/session/interfaces'; -import NormalizeService from '../utils/NormalizeService'; - -export class SessionPersistence { - static initialized() { - ServerDispatch.initialized(); - } - - static clearStore() { - ServerDispatch.clearStore(); - } - - static loginSuccessful(options: WebSocketConnectOptions) { - ServerDispatch.loginSuccessful(options); - } - - static loginFailed() { - ServerDispatch.loginFailed(); - } - - static connectionClosed(reason: number) { - ServerDispatch.connectionClosed(reason); - } - - static connectionFailed() { - ServerDispatch.connectionFailed(); - } - - static testConnectionSuccessful() { - ServerDispatch.testConnectionSuccessful(); - } - - static testConnectionFailed() { - ServerDispatch.testConnectionFailed(); - } - - static updateBuddyList(buddyList) { - ServerDispatch.updateBuddyList(buddyList); - } - - static addToBuddyList(user: User) { - ServerDispatch.addToBuddyList(user); - } - - static removeFromBuddyList(userName: string) { - ServerDispatch.removeFromBuddyList(userName); - } - - static updateIgnoreList(ignoreList) { - ServerDispatch.updateIgnoreList(ignoreList); - } - - static addToIgnoreList(user: User) { - ServerDispatch.addToIgnoreList(user); - } - - static removeFromIgnoreList(userName: string) { - ServerDispatch.removeFromIgnoreList(userName); - } - - static updateInfo(name: string, version: string) { - ServerDispatch.updateInfo(name, version); - } - - static updateStatus(state: number, description: string) { - ServerDispatch.updateStatus(state, description); - - if (state === StatusEnum.DISCONNECTED) { - this.connectionClosed(state); - } - } - - static updateUser(user: User) { - ServerDispatch.updateUser(user); - } - - static updateUsers(users: User[]) { - ServerDispatch.updateUsers(users); - } - - static userJoined(user: User) { - ServerDispatch.userJoined(user); - } - - static userLeft(userName: string) { - ServerDispatch.userLeft(userName); - } - - static serverMessage(message: string) { - ServerDispatch.serverMessage(sanitizeHtml(message)); - } - - static accountAwaitingActivation(options: WebSocketConnectOptions) { - ServerDispatch.accountAwaitingActivation(options); - } - - static accountActivationSuccess() { - ServerDispatch.accountActivationSuccess(); - } - - static accountActivationFailed() { - ServerDispatch.accountActivationFailed(); - } - - static registrationRequiresEmail() { - ServerDispatch.registrationRequiresEmail(); - } - - static registrationSuccess() { - ServerDispatch.registrationSuccess(); - } - - static registrationFailed(reason: string, endTime?: number) { - const reasonMsg = endTime ? NormalizeService.normalizeBannedUserError(reason, endTime) : reason; - - ServerDispatch.registrationFailed(reasonMsg); - } - - static registrationEmailError(error: string) { - ServerDispatch.registrationEmailError(error); - } - - static registrationPasswordError(error: string) { - ServerDispatch.registrationPasswordError(error); - } - - static registrationUserNameError(error: string) { - ServerDispatch.registrationUserNameError(error); - } - - static resetPasswordChallenge() { - ServerDispatch.resetPasswordChallenge(); - } - - static resetPassword() { - ServerDispatch.resetPassword(); - } - - static resetPasswordSuccess() { - ServerDispatch.resetPasswordSuccess(); - } - - static resetPasswordFailed() { - ServerDispatch.resetPasswordFailed(); - } - - static accountPasswordChange(): void { - ServerDispatch.accountPasswordChange(); - } - - static accountEditChanged(realName?: string, email?: string, country?: string): void { - ServerDispatch.accountEditChanged({ realName, email, country }); - } - - static accountImageChanged(avatarBmp: Uint8Array): void { - ServerDispatch.accountImageChanged({ avatarBmp }); - } - - static directMessageSent(userName: string, message: string): void { - ServerDispatch.directMessageSent(userName, message); - } - - static getUserInfo(userInfo: User) { - ServerDispatch.getUserInfo(userInfo); - } - - static getGamesOfUser(userName: string, response: any): void { - // Response_GetGamesOfUser contains a gameList field — log for now until game layer is complete - console.log('getGamesOfUser', userName, response); - } - - static gameJoined(gameJoinedData: GameJoinedData): void { - console.log('gameJoined', gameJoinedData); - } - - static notifyUser(notification: NotifyUserData): void { - ServerDispatch.notifyUser(notification); - } - - static playerPropertiesChanged(payload: PlayerGamePropertiesData): void { - console.log('playerPropertiesChanged', payload); - } - - static serverShutdown(data: ServerShutdownData): void { - ServerDispatch.serverShutdown(data); - } - - static userMessage(messageData: UserMessageData): void { - ServerDispatch.userMessage(messageData); - } - - static addToList(list: string, userName: string): void { - ServerDispatch.addToList(list, userName) - } - - static removeFromList(list: string, userName: string): void { - ServerDispatch.removeFromList(list, userName); - } - - static deleteServerDeck(deckId: number): void { - ServerDispatch.deckDelete(deckId); - } - - static updateServerDecks(deckList: DeckList): void { - ServerDispatch.backendDecks(deckList); - } - - static uploadServerDeck(path: string, treeItem: DeckStorageTreeItem): void { - ServerDispatch.deckUpload(path, treeItem); - } - - static createServerDeckDir(path: string, dirName: string): void { - ServerDispatch.deckNewDir(path, dirName); - } - - static deleteServerDeckDir(path: string): void { - ServerDispatch.deckDelDir(path); - } - - static replayList(matchList: ReplayMatch[]): void { - ServerDispatch.replayList(matchList); - } - - static replayAdded(matchInfo: ReplayMatch): void { - ServerDispatch.replayAdded(matchInfo); - } - - static replayModifyMatch(gameId: number, doNotHide: boolean): void { - ServerDispatch.replayModifyMatch(gameId, doNotHide); - } - - static replayDeleteMatch(gameId: number): void { - ServerDispatch.replayDeleteMatch(gameId); - } -} - - diff --git a/webclient/src/websocket/persistence/index.ts b/webclient/src/websocket/persistence/index.ts deleted file mode 100644 index a1e34fffa..000000000 --- a/webclient/src/websocket/persistence/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { AdminPersistence } from './AdminPersistence'; -export { RoomPersistence } from './RoomPersistence'; -export { SessionPersistence } from './SessionPersistence'; -export { ModeratorPersistence } from './ModeratorPersistence'; -export { GamePersistence } from './GamePersistence'; diff --git a/webclient/src/websocket/services/BackendService.ts b/webclient/src/websocket/services/BackendService.ts deleted file mode 100644 index fce741d35..000000000 --- a/webclient/src/websocket/services/BackendService.ts +++ /dev/null @@ -1,82 +0,0 @@ -import webClient from '../WebClient'; -import { ProtoController } from './ProtoController'; - -export interface CommandOptions { - responseName?: string; - onSuccess?: (response: any, raw: any) => void; - onError?: (responseCode: number, raw: any) => void; - onResponseCode?: { [code: number]: (raw: any) => void }; - onResponse?: (raw: any) => void; -} - -export class BackendService { - static sendSessionCommand(commandName: string, params: any, options: CommandOptions): void { - const command = ProtoController.root[commandName].create(params || {}); - const sc = ProtoController.root.SessionCommand.create({ - [`.${commandName}.ext`]: command, - }); - webClient.protobuf.sendSessionCommand(sc, raw => { - BackendService.handleResponse(commandName, raw, options); - }); - } - - static sendRoomCommand(roomId: number, commandName: string, params: any, options: CommandOptions): void { - const command = ProtoController.root[commandName].create(params || {}); - const rc = ProtoController.root.RoomCommand.create({ - [`.${commandName}.ext`]: command, - }); - webClient.protobuf.sendRoomCommand(roomId, rc, raw => { - BackendService.handleResponse(commandName, raw, options); - }); - } - - static sendModeratorCommand(commandName: string, params: any, options: CommandOptions): void { - const command = ProtoController.root[commandName].create(params || {}); - const mc = ProtoController.root.ModeratorCommand.create({ - [`.${commandName}.ext`]: command, - }); - webClient.protobuf.sendModeratorCommand(mc, raw => { - BackendService.handleResponse(commandName, raw, options); - }); - } - - static sendAdminCommand(commandName: string, params: any, options: CommandOptions): void { - const command = ProtoController.root[commandName].create(params || {}); - const ac = ProtoController.root.AdminCommand.create({ - [`.${commandName}.ext`]: command, - }); - webClient.protobuf.sendAdminCommand(ac, raw => { - BackendService.handleResponse(commandName, raw, options); - }); - } - - private static handleResponse(commandName: string, raw: any, options: CommandOptions): void { - if (options.onResponse) { - options.onResponse(raw); - return; - } - - const { responseCode } = raw; - - if (responseCode === ProtoController.root.Response.ResponseCode.RespOk) { - if (options.onSuccess) { - const response = options.responseName - ? raw[`.${options.responseName}.ext`] - : raw; - options.onSuccess(response, raw); - } - return; - } - - if (options.onResponseCode?.[responseCode]) { - options.onResponseCode[responseCode](raw); - return; - } - - if (options.onError) { - options.onError(responseCode, raw); - } else { - console.error(`${commandName} failed with response code: ${responseCode}`); - } - } -} diff --git a/webclient/src/websocket/services/KeepAliveService.spec.ts b/webclient/src/websocket/services/KeepAliveService.spec.ts deleted file mode 100644 index df5169b4c..000000000 --- a/webclient/src/websocket/services/KeepAliveService.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { KeepAliveService } from './KeepAliveService'; - -import webClient from '../WebClient'; - -describe('KeepAliveService', () => { - let service: KeepAliveService; - - beforeEach(() => { - jest.useFakeTimers(); - - service = new KeepAliveService(webClient.socket); - }); - - it('should create', () => { - expect(service).toBeDefined(); - }); - - describe('startPingLoop', () => { - let resolvePing; - let interval; - let promise; - let ping; - let checkReadyStateSpy; - - beforeEach(() => { - interval = 100; - promise = new Promise(resolve => resolvePing = resolve); - ping = (done) => promise.then(done); - - checkReadyStateSpy = jest.spyOn(webClient.socket, 'checkReadyState'); - checkReadyStateSpy.mockImplementation(() => true); - - service.startPingLoop(interval, ping); - jest.advanceTimersByTime(interval); - }); - - it('should start ping loop', () => { - expect((service as any).keepalivecb).toBeDefined(); - expect((service as any).lastPingPending).toBeTruthy(); - }); - - it('should call ping callback when done', (done: jest.DoneCallback) => { - resolvePing(); - - promise.then(() => { - expect((service as any).lastPingPending).toBeFalsy(); - done(); - }); - }); - - it('should fire disconnected$ if lastPingPending is still true', () => { - jest.spyOn(service.disconnected$, 'next').mockImplementation(() => {}); - jest.advanceTimersByTime(interval); - - expect(service.disconnected$.next).toHaveBeenCalled(); - }); - - it('should endPingLoop if socket is not open', () => { - jest.spyOn(service, 'endPingLoop').mockImplementation(() => {}); - checkReadyStateSpy.mockImplementation(() => false); - - resolvePing(); - jest.advanceTimersByTime(interval); - - expect(service.endPingLoop).toHaveBeenCalled(); - }); - }); -}); diff --git a/webclient/src/websocket/services/KeepAliveService.ts b/webclient/src/websocket/services/KeepAliveService.ts deleted file mode 100644 index 070a532e6..000000000 --- a/webclient/src/websocket/services/KeepAliveService.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Subject } from 'rxjs'; - -import { WebSocketService } from './WebSocketService'; - -export class KeepAliveService { - private socket: WebSocketService; - - private keepalivecb: NodeJS.Timeout; - private lastPingPending: boolean; - - public disconnected$ = new Subject(); - - constructor(socket: WebSocketService) { - this.socket = socket; - } - - public startPingLoop(interval: number, ping: Function): void { - this.keepalivecb = setInterval(() => { - // check if the previous ping got no reply - if (this.lastPingPending) { - this.disconnected$.next(); - } - - // stop the ping loop if we"re disconnected - if (!this.socket.checkReadyState(WebSocket.OPEN)) { - this.endPingLoop(); - return; - } - - this.lastPingPending = true; - ping(() => this.lastPingPending = false); - }, interval); - } - - public endPingLoop() { - clearInterval(this.keepalivecb); - this.keepalivecb = null; - this.lastPingPending = false; - } -} diff --git a/webclient/src/websocket/services/ProtoController.ts b/webclient/src/websocket/services/ProtoController.ts deleted file mode 100644 index 130d5e196..000000000 --- a/webclient/src/websocket/services/ProtoController.ts +++ /dev/null @@ -1,24 +0,0 @@ -import protobuf from 'protobufjs'; - -import { SessionPersistence } from '../persistence'; -import ProtoFiles from '../../proto-files.json'; - -const PB_FILE_DIR = `${process.env.PUBLIC_URL}/pb`; - -// Leaf module — no imports from the websocket layer other than persistence. -// Both BackendService and ProtobufService import this; neither should import -// the other for controller access, avoiding circular dependency cycles. -export const ProtoController = { - root: null as any, - - load(): void { - const files = ProtoFiles.map(file => `${PB_FILE_DIR}/${file}`); - ProtoController.root = new protobuf.Root(); - ProtoController.root.load(files, { keepCase: false }, (err: Error) => { - if (err) { - throw err; - } - SessionPersistence.initialized(); - }); - }, -}; diff --git a/webclient/src/websocket/services/ProtobufService.ts b/webclient/src/websocket/services/ProtobufService.ts deleted file mode 100644 index 20c4e8599..000000000 --- a/webclient/src/websocket/services/ProtobufService.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { CommonEvents, GameEvents, RoomEvents, SessionEvents } from '../events'; -import { WebClient } from '../WebClient'; -import { SessionCommands } from 'websocket'; -import { ProtoController } from './ProtoController'; - -export interface ProtobufEvents { - [event: string]: Function; -} - -export class ProtobufService { - private cmdId = 0; - private pendingCommands: { [cmdId: string]: Function } = {}; - - private webClient: WebClient; - - constructor(webClient: WebClient) { - this.webClient = webClient; - ProtoController.load(); - } - - public resetCommands() { - this.cmdId = 0; - this.pendingCommands = {}; - } - - public sendRoomCommand(roomId: number, roomCmd: any, callback?: Function) { - const cmd = ProtoController.root.CommandContainer.create({ - 'roomId': roomId, - 'roomCommand': [roomCmd] - }); - - this.sendCommand(cmd, raw => callback && callback(raw)); - } - - public sendSessionCommand(sesCmd: any, callback?: Function) { - const cmd = ProtoController.root.CommandContainer.create({ - 'sessionCommand': [sesCmd] - }); - - this.sendCommand(cmd, (raw) => callback && callback(raw)); - } - - public sendModeratorCommand(modCmd: any, callback?: Function) { - const cmd = ProtoController.root.CommandContainer.create({ - 'moderatorCommand': [modCmd] - }); - - this.sendCommand(cmd, (raw) => callback && callback(raw)); - } - - public sendAdminCommand(adminCmd: any, callback?: Function) { - const cmd = ProtoController.root.CommandContainer.create({ - 'adminCommand': [adminCmd] - }); - - this.sendCommand(cmd, (raw) => callback && callback(raw)); - } - - public sendCommand(cmd: any, callback: Function) { - this.cmdId++; - - cmd['cmdId'] = this.cmdId; - this.pendingCommands[this.cmdId] = callback; - - if (this.webClient.socket.checkReadyState(WebSocket.OPEN)) { - this.webClient.socket.send(ProtoController.root.CommandContainer.encode(cmd).finish()); - } - } - - public sendKeepAliveCommand(pingReceived: Function) { - SessionCommands.ping(pingReceived); - } - - public handleMessageEvent({ data }: MessageEvent): void { - try { - const uint8msg = new Uint8Array(data); - const msg = ProtoController.root.ServerMessage.decode(uint8msg); - - if (msg) { - switch (msg.messageType) { - case ProtoController.root.ServerMessage.MessageType.RESPONSE: - this.processServerResponse(msg.response); - break; - case ProtoController.root.ServerMessage.MessageType.ROOM_EVENT: - this.processRoomEvent(msg.roomEvent, msg); - break; - case ProtoController.root.ServerMessage.MessageType.SESSION_EVENT: - this.processSessionEvent(msg.sessionEvent, msg); - break; - case ProtoController.root.ServerMessage.MessageType.GAME_EVENT_CONTAINER: - this.processGameEvent(msg.gameEvent, msg); - break; - default: - console.log(msg); - break; - } - } - } catch (err) { - console.error('Processing failed:', err); - } - } - - private processServerResponse(response: any) { - const { cmdId } = response; - - if (this.pendingCommands[cmdId]) { - this.pendingCommands[cmdId](response); - delete this.pendingCommands[cmdId]; - } - } - - private processCommonEvent(response: any, raw: any) { - this.processEvent(response, CommonEvents, raw); - } - - private processRoomEvent(response: any, raw: any) { - this.processEvent(response, RoomEvents, raw); - } - - private processSessionEvent(response: any, raw: any) { - this.processEvent(response, SessionEvents, raw); - } - - private processGameEvent(response: any, raw: any): void { - this.processEvent(response, GameEvents, raw); - } - - private processEvent(response: any, events: ProtobufEvents, raw: any) { - for (const event in events) { - const payload = response[event]; - - if (payload) { - events[event](payload, raw); - return; - } - } - } -} diff --git a/webclient/src/websocket/services/WebSocketService.ts b/webclient/src/websocket/services/WebSocketService.ts deleted file mode 100644 index 95fa2d356..000000000 --- a/webclient/src/websocket/services/WebSocketService.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Subject } from 'rxjs'; - -import { StatusEnum, WebSocketConnectOptions } from 'types'; - -import { KeepAliveService } from './KeepAliveService'; -import { WebClient } from '../WebClient'; -import { SessionPersistence } from '../persistence'; -import { updateStatus } from '../commands/session'; - -export class WebSocketService { - private socket: WebSocket; - private testSocket: WebSocket; - - private webClient: WebClient; - private keepAliveService: KeepAliveService; - - public message$: Subject = new Subject(); - - private keepalive: number; - - constructor(webClient: WebClient) { - this.webClient = webClient; - - this.keepAliveService = new KeepAliveService(this); - this.keepAliveService.disconnected$.subscribe(() => { - this.disconnect(); - updateStatus(StatusEnum.DISCONNECTED, 'Connection timeout'); - }); - } - - public connect(options: WebSocketConnectOptions, protocol: string = 'wss'): void { - if (window.location.hostname === 'localhost') { - protocol = 'ws'; - } - - const { host, port } = options; - this.keepalive = this.webClient.clientOptions.keepalive; - - this.socket = this.createWebSocket(`${protocol}://${host}:${port}`); - } - - public testConnect(options: WebSocketConnectOptions, protocol: string = 'wss'): void { - if (window.location.hostname === 'localhost') { - protocol = 'ws'; - } - - const { host, port } = options; - - this.testWebSocket(`${protocol}://${host}:${port}`); - } - - public disconnect(): void { - if (this.socket) { - this.socket.close(); - } - } - - public checkReadyState(state: number): boolean { - return this.socket?.readyState === state; - } - - public send(message): void { - this.socket.send(message); - } - - private createWebSocket(url: string): WebSocket { - const socket = new WebSocket(url); - socket.binaryType = 'arraybuffer'; - - const connectionTimer = setTimeout(() => socket.close(), this.keepalive); - - socket.onopen = () => { - clearTimeout(connectionTimer); - updateStatus(StatusEnum.CONNECTED, 'Connected'); - - this.keepAliveService.startPingLoop(this.keepalive, (pingReceived: Function) => { - this.webClient.keepAlive(pingReceived); - }); - }; - - socket.onclose = () => { - // dont overwrite failure messages - if (this.webClient.status !== StatusEnum.DISCONNECTED) { - updateStatus(StatusEnum.DISCONNECTED, 'Connection Closed'); - } - - this.keepAliveService.endPingLoop(); - }; - - socket.onerror = () => { - updateStatus(StatusEnum.DISCONNECTED, 'Connection Failed'); - SessionPersistence.connectionFailed(); - }; - - socket.onmessage = (event: MessageEvent) => { - this.message$.next(event); - } - - return socket; - } - - private testWebSocket(url: string): void { - if (this.testSocket) { - this.testSocket.onerror = null; - this.testSocket.close(); - } - - const socket = new WebSocket(url); - socket.binaryType = 'arraybuffer'; - - const connectionTimer = setTimeout(() => socket.close(), this.webClient.clientOptions.keepalive); - - socket.onopen = () => { - clearTimeout(connectionTimer); - SessionPersistence.testConnectionSuccessful(); - socket.close(); - }; - - socket.onerror = () => { - SessionPersistence.testConnectionFailed(); - }; - - socket.onclose = () => { - this.testSocket = null; - } - - this.testSocket = socket; - } -} diff --git a/webclient/src/websocket/utils/NormalizeService.ts b/webclient/src/websocket/utils/NormalizeService.ts deleted file mode 100644 index 4d9d37ba6..000000000 --- a/webclient/src/websocket/utils/NormalizeService.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Game, GametypeMap, LogItem, LogGroups, Message, Room } from 'types'; - -export default class NormalizeService { - // Flatten room gameTypes into map object - static normalizeRoomInfo(roomInfo: Room): void { - roomInfo.gametypeMap = {}; - - const { gametypeList, gametypeMap, gameList } = roomInfo; - - gametypeList.reduce((map, type) => { - map[type.gameTypeId] = type.description; - return map; - }, gametypeMap); - - gameList.forEach((game) => NormalizeService.normalizeGameObject(game, gametypeMap)); - } - - // Flatten gameTypes[] into gameType field - // Default sortable values ("" || 0 || -1) - static normalizeGameObject(game: Game, gametypeMap: GametypeMap): void { - const { gameTypes, description } = game; - const hasType = gameTypes && gameTypes.length; - game.gameType = hasType ? gametypeMap[gameTypes[0]] : ''; - - game.description = description || ''; - } - - // Flatten logs[] into object mapped by targetType (room, game, chat) - static normalizeLogs(logs: LogItem[]): LogGroups { - return logs.reduce((obj, log) => { - const { targetType } = log; - obj[targetType] = obj[targetType] || []; - obj[targetType].push(log); - return obj; - }, {} as LogGroups); - } - - // messages sent by current user dont have their username prepended - static normalizeUserMessage(message: Message): void { - const { name } = message; - - if (name) { - message.message = `${name}: ${message.message}`; - } - } - - // Banned reason string is not being exposed by the server - static normalizeBannedUserError(reasonStr: string, endTime: number): string { - let error; - - if (endTime) { - error = 'You are banned until ' + new Date(endTime).toString(); - } else { - error = 'You are permanently banned'; - } - - if (reasonStr) { - error += '\n\n' + reasonStr; - } - - return error; - } -} diff --git a/webclient/src/websocket/utils/guid.util.ts b/webclient/src/websocket/utils/guid.util.ts deleted file mode 100644 index 73b17d719..000000000 --- a/webclient/src/websocket/utils/guid.util.ts +++ /dev/null @@ -1,8 +0,0 @@ -function s4(): string { - const s4 = Math.floor((1 + Math.random()) * 0x10000); - return s4.toString(16).substring(1); -} - -export function guid(): string { - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); -} diff --git a/webclient/src/websocket/utils/index.ts b/webclient/src/websocket/utils/index.ts deleted file mode 100644 index 4147f4af8..000000000 --- a/webclient/src/websocket/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './guid.util'; -export * from './sanitizeHtml.util'; -export * from './passwordHasher'; diff --git a/webclient/src/websocket/utils/passwordHasher.ts b/webclient/src/websocket/utils/passwordHasher.ts deleted file mode 100644 index 164a91823..000000000 --- a/webclient/src/websocket/utils/passwordHasher.ts +++ /dev/null @@ -1,32 +0,0 @@ -import sha512 from 'crypto-js/sha512'; -import Base64 from 'crypto-js/enc-base64'; -import { ProtoController } from '../services/ProtoController'; - -const HASH_ROUNDS = 1_000; -const SALT_LENGTH = 16; - -export const hashPassword = (salt: string, password: string): string => { - let hashedPassword = salt + password; - for (let i = 0; i < HASH_ROUNDS; i++) { - // WHY DO WE DO IT THIS WAY? - hashedPassword = sha512(hashedPassword); - } - - return salt + Base64.stringify(hashedPassword); -}; - -export const generateSalt = (): string => { - const characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - - let salt = ''; - for (let i = 0; i < SALT_LENGTH; i++) { - salt += characters.charAt(Math.floor(Math.random() * characters.length)); - } - - return salt; -} - -export const passwordSaltSupported = (serverOptions: number): number => { - // Intentional use of Bitwise operator b/c of how Servatrice Enums work - return serverOptions & ProtoController.root.Event_ServerIdentification.ServerOptions.SupportsPasswordHash; -} diff --git a/webclient/src/websocket/utils/sanitizeHtml.util.ts b/webclient/src/websocket/utils/sanitizeHtml.util.ts deleted file mode 100644 index e5321213b..000000000 --- a/webclient/src/websocket/utils/sanitizeHtml.util.ts +++ /dev/null @@ -1,17 +0,0 @@ -import sanitize from 'sanitize-html'; - -export function sanitizeHtml(msg: string): string { - return sanitize(msg, { - allowedTags: ['br', 'a', 'img', 'center', 'b', 'font'], - allowedAttributes: { - '*': ['href', 'color', 'rel', 'target'], - }, - allowedSchemes: ['http', 'https', 'ftp'], - transformTags: { - 'a': sanitize.simpleTransform('a', { - target: '_blank', - rel: 'noopener noreferrer', - }), - } - }); -} diff --git a/webclient/tsconfig.json b/webclient/tsconfig.json deleted file mode 100644 index 0e35af354..000000000 --- a/webclient/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "src", - "target": "es6", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "typeRoots": [ - "node_modules/@types" - ], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "noFallthroughCasesInSwitch": false - }, - "include": [ - "src" - ] -}